【BOOST C++ 6 】通信(04 协程 )

发布于:2022-11-27 ⋅ 阅读:(255) ⋅ 点赞:(0)

一、关于协程

        从 1.54.0 版本开始,Boost.Asio 支持协程。虽然您可以直接使用 Boost.Coroutine,但 Boost.Asio 中对协程的显式支持使得使用它们变得更加容易。

        协程让您创建一个反映实际程序逻辑的结构。异步操作不会拆分函数,因为没有处理程序来定义异步操作完成时应该发生什么。程序可以使用顺序结构,而不是让处理程序相互调用。

二、与线程的区别

        线程处于进程之中,协程处于线程之中,线程有系统内核调度,而协程有程序员自己调度。一个线程可以有多个协程,而且只要内存足够,一个线程中可以有任意多个协程;但某一时刻只能有一个协程在运行,多个协程分享该线程分配到的计算机资源。协程是追求极限性能和优美的代码结构的产物。

使用过程中需要包含#include <boost/coroutine2/all.hpp>,链接动态库:-lboost_coroutine -lboost_context。关于使用boost库错

协程有如下特点:

  1. 同其他数据类型一样,协程也是第一类(first-class)对象,可以被当参数传递等操作;
  2. 运行特点是挂起运行,离开协程,过后再进入,恢复运行;
  3. 具有对称和非对称的转移控制机制
  4. 挂起前和恢复后本地变量的值是一致的;
  5. 有stackless和stackful两种类型

三、协程示例

示例 32.7。使用 Boost.Asio 的协程
#include <boost/asio/io_service.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/asio/write.hpp>
#include <boost/asio/buffer.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <list>
#include <string>
#include <ctime>

using namespace boost::asio;
using namespace boost::asio::ip;

io_service ioservice;
tcp::endpoint tcp_endpoint{tcp::v4(), 2014};
tcp::acceptor tcp_acceptor{ioservice, tcp_endpoint};
std::list<tcp::socket> tcp_sockets;

void do_write(tcp::socket &tcp_socket, yield_context yield)
{
  std::time_t now = std::time(nullptr);
  std::string data = std::ctime(&now);
  async_write(tcp_socket, buffer(data), yield);
  tcp_socket.shutdown(tcp::socket::shutdown_send);
}

void do_accept(yield_context yield)
{
  for (int i = 0; i < 2; ++i)
  {
    tcp_sockets.emplace_back(ioservice);
    tcp_acceptor.async_accept(tcp_sockets.back(), yield);
    spawn(ioservice, [](yield_context yield)
      { do_write(tcp_sockets.back(), yield); });
  }
}

int main()
{
  tcp_acceptor.listen();
  spawn(ioservice, do_accept);
  ioservice.run();
}

        调用 Boost.Asio 使用协程的函数是 boost::asio::spawn()。传递的第一个参数必须是 I/O 服务对象。第二个参数是将成为协程的函数。此函数必须接受 boost::asio::yield_context 类型的对象作为其唯一参数。它必须没有返回值。示例 32.7 使用 do_accept() 和 do_write() 作为协程。如果函数签名不同,例如 do_write() 的情况,您必须使用类似 std::bind 的适配器或 lambda 函数。

        您可以将 boost::asio::yield_context 类型的对象而不是处理程序传递给异步函数。 do_accept() 将参数 yield 传递给 async_accept()。在 do_write() 中,yield 被传递给 async_write()。这些函数调用仍会启动异步操作,但在操作完成时不会调用任何处理程序。而是恢复启动异步操作的上下文。当这些异步操作完成时,程序会从中断的地方继续。

        do_accept() 包含一个 for 循环。每次调用该函数时,都会将一个新套接字传递给 async_accept()。一旦客户端建立连接,do_write() 将作为协程调用,并带有 boost::asio::spawn() 以将当前时间发送给客户端。

        for 循环可以很容易地看出程序在退出之前可以为两个客户端提供服务。由于该示例基于协程,因此可以在 for 循环中实现异步操作的重复执行。这提高了程序的可读性,因为您不必跟踪对处理程序的潜在调用来找出最后一个异步操作何时完成。如果时间服务器需要支持两个以上的客户端,则只需调整 for 循环。


网站公告

今日签到

点亮在社区的每一天
去签到