文档地址
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
是成员变量的话
reenter (coro_)
{
}
当一段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