IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    boost::asio::coroutine 文档翻译 + 源码解析

    Khan发表于 2017-10-14 04:26:00
    love 0

    文档地址

    http://www.boost.org/doc/libs/1_60_0/doc/html/boost_asio/reference/coroutine.html

    文档翻译

    协程

    提供实现不需要栈的协程的支持

    class coroutine

    成员函数

    函数名 描述
    coroutine 构造成为初始化状态
    is_child 如果是一个fork子协程的话返回true
    is_complete 如果到了终止状态就返回true
    is_parent 如果是fork的父协程的话返回true

    coroutine 类可以用来实现无栈的协程。这个类自身被用来保存协程的状态。
    coroutine 类可以支持复制构造和赋值。最大一个int的空间占用。可以当作基类使用

    class session : coroutine
    {
     // 。。。
    };

    或者作为一个数据项。

    class session
    {
      //。。。
      coroutine coro_;
    };

    或者设置作为一个lambda或者bind()的参数。 这种实现的重点是在协程存在的时候,这个对下必须不被释放。

    伪关键字

    协程是联合特定伪关键子使用的,这些伪关键字使用宏实现。这些宏被定义在
    #include <boost/asio/yield.hpp>

    可以通过下面的头文件方便的undefine

    #include <boost/asio/unyield.hpp>

    reenter

    reenter宏是用来定义一段协程的。他仅仅接收1个参数 : 一个coroutine的指针或者引用。比如 , 如果基类是coroutine你可以这样写 :

    reenter (this)
    {
      //coroutine body 
    }

    如果coroutine是成员变量的话

    reenter (coro_)
    {
      // coroutine body 
    }

    当一段reenter代码被执行的时候,直接跳转到最近的一次yeild或者fork位置后执行。
    协程代码段也可以是一个单独的语句

    reenter (this) for (;;)
    {
      //。。。
    }

    局限性: 由于reenter宏是用swtich来实现的,所以你在协程代码中定义局部变量的时候必须格外注意。 这个变量不能定义在重入的时候会被跳过的地方。

    yield 语句

    这种格式的 yield 关键字通常被用来做异步操作 :

    yield socket_->async_read_some(buffer(*buffer_), *this);

    这个分成4步骤实现:

    • yield 保存当前协程的状态.
    • 初始化异步操作。
    • 继续执行点被设置为下一行。
    • 控制跳转到协程末尾,推出协程.。

    当异步操作完成后,再次执行这个协程。重入后从继续执行点执行。记住,异步操作执行后再次调用协程是很重要的。

    1 yield
    2 {
    3   mutable_buffers_1 b = buffer(*buffer_);
    4   socket_->async_read_some(b, *this);
    5 }

    yield return expression ;

    这种形式通常被用来做基于协程的解析器。比如 :

     1 struct interleave : coroutine
     2 {
     3   istream& is1;
     4   istream& is2;
     5   char operator()(char c)
     6   {
     7     reenter (this) for (;;)
     8     {
     9       yield return is1.get();
    10       yield return is2.get();
    11     }
    12   }
    13 };

    定义了一个小协程来间隔的从两个流中读取数据,

    这个分成3步骤实现

    • yield 保存当前协程的状态.
    • 继续执行点被设置为下一行。
    • 函数返回表达式的值

    yield ;

    This form of yield is equivalent to the following steps:

    这个分成3步骤实现

    • yield 保存当前协程的状态.
    • 继续执行点被设置为紧跟着分号。
    • 控制函数到达代码末尾。
      这种格式在协程被用来组织线程的合作和调度的时候。比如 :
     1 struct task : coroutine
     2 {
     3   //。。。
     4   void operator()()
     5   {
     6     reenter (this)
     7     {
     8       while ( not finished )
     9       {
    10          do something 
    11         yield;
    12          do some more 
    13         yield;
    14       }
    15     }
    16   }
    17   //。。。
    18 };
    19 //。。。
    20 task t1, t2;
    21 for (;;)
    22 {
    23   t1();
    24   t2();
    25 }

    yield break ;

    最后一种格式是用来显示的终止协程。分成两步骤:

    • yeild将协程状态设置为终止。
    • 控制函数到达代码末尾。

    一旦协程终止。调用 is_complete()返回true 。 协程不能再被重入。
    注意 : 当协程代码块被显示终止的时候,比如return , 抛出异常或者运行到结尾的时候,都会被设置为终止。

    fork statement

    fork伪关键字是用来使得一个协程分支的。它将一个协程分成两个(或者更多)复制。一个使用场景是在服务器,产生一个新的协程来处理每个客户端的链接。

     1 reenter (this)
     2 {
     3   do
     4   {
     5     socket_.reset(new tcp::socket(io_service_));
     6     yield acceptor->async_accept(*socket_, *this);
     7     fork server(*this)();
     8   } while (is_parent());
     9   // client-specific handling follows 
    10 }

    这个分4个步骤实现 :

    • fork 保存当前的协程状态.
    • 创建一个协程的复制,要么立刻执行它,要么呆会。
    • 回复点紧跟着分号之后.
    • 对于父类,回复点在下一行。

    函数is_parenet()和is_child()可以被用来区分父协程和自协程。你可以用他们来改变后面的工作顺序。
    注意: fork 关键字并不真的实现分支。是程序来创建一个新的副本并调用它。你可以像上面那样立刻执行,也可以利用类似io_server::post() 这样的接口来延迟执行。

    替代的宏

    • BOOST_ASIO_CORO_REENTER instead of reenter
    • BOOST_ASIO_CORO_YIELD instead ofyield
    • BOOST_ASIO_CORO_FORK instead of fork

    源码解析

    来源 :

    boost/asio/coroutine.hpp

     1 // 定义 coroutine 类, 本质上是一个行号记录类。
     2 // 行号是这个东西冲入的唯一依据。
     3 class coroutine
     4 {
     5 public:
     6   // 初始化0
     7   /// Constructs a coroutine in its initial state.
     8   coroutine() : value_(0) {}
     9 
    10   // fork 的子协程初始行号是当前行号的负数。因此判断它是否为负数。
    11   // 当子协程支持再fork后,value_变成新的行号,便不再被认为是child。 
    12   /// Returns true if the coroutine is the child of a fork.
    13   bool is_child() const { return value_ < 0; }
    14   // 返回 ! is_child()
    15   /// Returns true if the coroutine is the parent of a fork.
    16   bool is_parent() const { return !is_child(); }
    17 
    18   // 当reenter宏包被的模块(里面应该有yeild或者fork , 否则这个模块仅仅是普通的代码块,永远不存在结束状态)执行结束的时候,vaule_被设置为-1。
    19   /// Returns true if the coroutine has reached its terminal state.
    20   bool is_complete() const { return value_ == -1; }
    21 
    22 private:
    23   friend class detail::coroutine_ref;
    24   int value_;
    25 };
    26 
    27 
    28 namespace detail {
    29 // 引用类,使用这个类来方便的修改& 检测coroutine 类的值。
    30 class coroutine_ref
    31 {
    32 public:
    33   coroutine_ref(coroutine& c) : value_(c.value_), modified_(false) {}
    34   coroutine_ref(coroutine* c) : value_(c->value_), modified_(false) {}
    35   ~coroutine_ref() { if (!modified_) value_ = -1; }
    36   operator int() const { return value_; }
    37   int& operator=(int v) { modified_ = true; return value_ = v; }
    38 private:
    39   void operator=(const coroutine_ref&);
    40   int& value_;
    41   bool modified_;
    42 };
    43 
    44 } // namespace detail
    45 } // namespace asio
    46 } // namespace boost
    47 
    48 #define BOOST_ASIO_CORO_REENTER(c) \
    49   switch (::boost::asio::detail::coroutine_ref _coro_value = c)\
    50     case -1: if (_coro_value) \
    51     { \
    52       goto terminate_coroutine; \
    53       terminate_coroutine: /*这是标记reenter模块结束的清理代码*/\
    54       _coro_value = -1; \
    55       goto bail_out_of_coroutine; /*退出这次执行*/\
    56       bail_out_of_coroutine: \
    57       break; \
    58     } \
    59     else case 0: /*下面是我们的代码块*/
    60 
    61 #define BOOST_ASIO_CORO_YIELD_IMPL(n) \
    62   for (_coro_value = (n);;) \
    63     if (_coro_value == 0) \
    64     { \
    65       case (n): /*当reenter模块被重入的时候,根据行号直接跳转到这里从而直接执行下一行*/; \
    66       break; \
    67     } \
    68     else /*第一次执行到这里*/\
    69       switch (_coro_value ? 0 : 1) \
    70         for (;;)  \
    71           case -1: if (_coro_value)/*执行yeild break 终止 */ \
    72             goto terminate_coroutine; \
    73           else for (;;)/*执行yeild 而不是 yeild return 的话,循环 */\
    74             case 1: if (_coro_value) \
    75               goto bail_out_of_coroutine; \
    76             else case 0:
    77 
    78 #define BOOST_ASIO_CORO_FORK_IMPL(n) \
    79   for (_coro_value = -(n);; _coro_value = (n))/*这个循环其实仅仅执行两次 : core_calue == -n 执行子协程和 core_value == n 执行父协程*/ \
    80     if (_coro_value == (n)) \
    81     { \
    82       case -(n): ; \
    83       break; \
    84     } \
    85     else
    86 
    87 #if defined(_MSC_VER)
    88 # define BOOST_ASIO_CORO_YIELD BOOST_ASIO_CORO_YIELD_IMPL(__COUNTER__ + 1)
    89 # define BOOST_ASIO_CORO_FORK BOOST_ASIO_CORO_FORK_IMPL(__COUNTER__ + 1)
    90 #else // defined(_MSC_VER)
    91 # define BOOST_ASIO_CORO_YIELD BOOST_ASIO_CORO_YIELD_IMPL(__LINE__)
    92 # define BOOST_ASIO_CORO_FORK BOOST_ASIO_CORO_FORK_IMPL(__LINE__)
    93 #endif // defined(_MSC_VER)
    94 
    95 #endif // BOOST_ASIO_COROUTINE_HPP
    96 




    Khan 2017-10-14 12:26 发表评论


沪ICP备19023445号-2号
友情链接