boost无栈协程

boost 1_66 无栈协程 coroutine 学习
安装boost库
yum直接安装:
1、yum install boost
2、yum install boost-devel
3、yum install boosat-doc
PS:注意yum安装的版本,有些旧boost版本还没有协程库

手动编译安装 boost_1_66_0.tar.gz
1、tar -vxzf boost_1_66_0.tar.gz
2、cd boost_1_66_0
3、./bootstrap.sh
4、./b2 install --prefix=/usr
5、ldconfig -v

检验是否安装成功

#include <iostream>
#include <boost/version.hpp>
#include <boost/config.hpp>

using namespace std;

int main()
{
  cout << "BOOST_VERSION: " << BOOST_VERSION << endl;
  cout << "BOOST_LIB_VERSION: " << BOOST_LIB_VERSION << endl;
  cout << "BOOST_PLATFORM: " << BOOST_PLATFORM << endl;
  cout << "BOOST_COMPILER: " << BOOST_COMPILER << endl;
  cout << "BOOST_STDLIB: " << BOOST_STDLIB << endl;
  return 0;
}

执行结果

BOOST_VERSION: 106600
BOOST_LIB_VERSION: 1_66
BOOST_PLATFORM: linux
BOOST_COMPILER: GNU C++ version 4.8.5 20150623 (Red Hat 4.8.5-44)
BOOST_STDLIB: GNU libstdc++ version 20150623

安装成功

boost官网关于无栈协程介绍
主要来源于boost官网关于stackless协程的介绍

类成员含义
coroutine构造成为初始化状态
is_child如果是一个fork子协程的话返回true
is_complete如果到了终止状态就返回true
is_parent如果是fork的父协程的话返回true

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

reenter
用来定义一段协程,只接受一个coroutine的指针或引用,鉴于coroutine本身可以当作基类,你也可以用类似reenter(this) {…}的写法,当reenter被执行的时候,控制流会跳转到最后yield或者fork的位置。需要注意的是reenter宏是通过switch实现的,并不是类似线程那种上下文的保存和切换达到的,意味着当在协程体中使用局部变量的时候,重入协程体时候不能忽略局部变量的定义。

yield {statement}
常常用在异步操作的时候,其执行的逻辑为:yield保存了当前协程的状态;其表达式初始化了异步操作;定义恢复点为statement后的一条语句;控制流被转移到了协程体的结尾。当异步操作结束的时候,函数对象重新被唤醒,然后reenter使得执行流转移到了恢复点。当然statement表达式也可以是复合表达式,用花括号框起,见boost_demo2

yield return {expression}
通常用于生成器的环境下使用,其return后面的值作为函数的返回值传递出来,同时不会继续执行reenter后的代码

yield break
主要用来终止协程的,yield首先设置协程的终止状体,然后流程被转移到了协程体的结尾。一旦终止,使用is_complete()就会返回true,同时协程不能够被再次reenter了。当然不一定要yield break,当流程执行到了协程体结尾,这些协程也会自动terminate了。这也很好理解,因为stackless协程的reenter本来就是用switch实现的。

fork {statement}
可以创建多个协程的拷贝,常用的情况是在服务端,协程被fork出来用于处理客户端的请求。父协程和子协程通过is_parent()、is_child()进行界定。

无栈协程源码
boost库中无栈协程的源码非常简洁明了且和boost其他的模块没有任何耦合,完全可以将该部分提出来单独使用,其源码只在boost/asio/coroutine.hpp和boost/asio/yield.hpp中,这里默认在Linux环境下运行,去掉一些编译相关的选项,源码只有如下这部分

include <iostream>
//coroutine_basic.h

class coroutine_ref;

class coroutine
{
public:
  /// Constructs a coroutine in its initial state.
  coroutine() : value_(0) {}

  /// Returns true if the coroutine is the child of a fork.
  bool is_child() const { return value_ < 0; }

  /// Returns true if the coroutine is the parent of a fork.
  bool is_parent() const { return !is_child(); }

  /// Returns true if the coroutine has reached its terminal state.
  bool is_complete() const { return value_ == -1; }

private:
  friend class coroutine_ref;
  int value_;
};

class coroutine_ref
{
public:
  coroutine_ref(coroutine& c) : value_(c.value_), modified_(false) {}
  coroutine_ref(coroutine* c) : value_(c->value_), modified_(false) {}
  ~coroutine_ref() { if (!modified_) value_ = -1; }
  operator int() const { return value_; }
  int& operator=(int v) { modified_ = true; return value_ = v; }
private:
  void operator=(const coroutine_ref&);
  int& value_;
  bool modified_;
};

#define BOOST_ASIO_CORO_REENTER(c) \
  switch (coroutine_ref _coro_value = c) \
    case -1: if (_coro_value) \
    { \
      goto terminate_coroutine; \
      terminate_coroutine: \
      _coro_value = -1; \
      goto bail_out_of_coroutine; \
      bail_out_of_coroutine: \
      break; \
    } \
    else /* fall-through */ case 0:

#define BOOST_ASIO_CORO_YIELD_IMPL(n) \
  for (_coro_value = (n);;) \
    if (_coro_value == 0) \
    { \
      case (n): ; \
      break; \
    } \
    else \
      switch (_coro_value ? 0 : 1) \
        for (;;) \
          /* fall-through */ case -1: if (_coro_value) \
            goto terminate_coroutine; \
          else for (;;) \
            /* fall-through */ case 1: if (_coro_value) \
              goto bail_out_of_coroutine; \
            else /* fall-through */ case 0: \

#define BOOST_ASIO_CORO_FORK_IMPL(n) \
  for (_coro_value = -(n);; _coro_value = (n)) \
    if (_coro_value == (n)) \
    { \
      case -(n): ; \
      break; \
    } \
    else

# define BOOST_ASIO_CORO_YIELD BOOST_ASIO_CORO_YIELD_IMPL(__LINE__)
# define BOOST_ASIO_CORO_FORK BOOST_ASIO_CORO_FORK_IMPL(__LINE__)

# define reenter(c) BOOST_ASIO_CORO_REENTER(c)
# define yield BOOST_ASIO_CORO_YIELD
# define fork BOOST_ASIO_CORO_FORK

在了解无栈协程的源码前,可以先了解一种C语言的编程技巧----达夫设备 Duff’s Device,其本意是通过程序员手动展开代码以减少循环次数,利用switch-case中case标签的fall-through特性,随着编译器的发展,现在很多编译器已经能自动去做这样一件事情了,可这个编程思路还是有助于我们实现一种朴素的协程,也就是上面代码中无栈协程的实现

#include "coroutine_basic.h" //上文中从boost库提取的无栈协程源码
#include <iostream>
using namespace std;

coroutine c;

void foo(int i)
{
  cout << "before reenter " << i << endl;
  reenter(c)
  {
    yield cout << "foo1 " << i+1 << endl;
    yield cout << "foo2 " << i+1 << endl;
  }
  cout << "after reenter" << i << endl;
}
int main()
{
  foo(1);
  foo(2);
  foo(3);
  return 0;
}

运行结果

before reenter 1
foo1 2
after reenter1
before reenter 2
foo2 3
after reenter2
before reenter 3
after reenter3

看下该段示例代码的预编译后的foo函数代码片段

void foo(int i)
{
  cout << "before reenter " << i << endl;
  switch (coroutine_ref _coro_value = c) case -1: if (_coro_value) { goto terminate_coroutine; terminate_coroutine: _coro_value = -1; goto bail_out_of_coroutine; bail_out_of_coroutine: break; } else case 0:
  {
    for (_coro_value = (12);;) if (_coro_value == 0) { case (12): ; break; } else switch (_coro_value ? 0 : 1) for (;;) case -1: if (_coro_value) goto terminate_coroutine; else for (;;) case 1: if (_coro_value) goto bail_out_of_coroutine; else case 0: cout << "foo1 " << i+1 << endl;
    for (_coro_value = (13);;) if (_coro_value == 0) { case (13): ; break; } else switch (_coro_value ? 0 : 1) for (;;) case -1: if (_coro_value) goto terminate_coroutine; else for (;;) case 1: if (_coro_value) goto bail_out_of_coroutine; else case 0: cout << "foo2 " << i+1 << endl;
  }
  cout << "after reenter" << i << endl;
}

可以看到,可重入是通过传入函数的引用coroutine_ref _coro_value改变了全局变量coroutine c的value值,第二次进入foo函数后直接通过swith(12) break执行第13行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值