coroutine, it's fun

协程这东西虽然前面已经讲过很多次了,不过都是讲的有栈协程,这里说说C++20的无栈协程。

C++20引入了协程,带来了三个关键字co_awaitco_yield以及co_return ,两个结构coroutine_traitscoroutine_handle 。按照标准的定义,如果一个函数体内使用了以上提到的任意一个关键字,那么这个函数就是一个coroutine,main函数不能是coroutine。和其他语言提供的协程相比C++提供的更加底层,优点是库作者有更多发挥的空间,可以用来实现各种类型的coroutine,比如 生成器,go协程等等;缺点是对普通用户来说无法应用,对库作者来说需要主要更多的细节。

一些概念

coroutine_traits

可以看作是一个约束,实际上长这样

template<typename R, typename... Args> struct coroutine_traits;

协程的类型应该是

R::promise_type coroutine(Args&&...)

coroutine_handle

可以看作是编译器暴露的接口,用户可以利用这个handle恢复(resume)协程,也可以通过from_promise()promise() 两个函数和promise_type之间转化。

awaitable

我们看看标准自带的suspend_always 的定义:

struct suspend_always
{
  bool await_ready() { return false; }

  void await_suspend(coroutine_handle<>) {}

  void await_resume() {}
};

如果一个类型包含上面三个成员函数,或者定义了operator co_await返回这样的类型,这里称这些类型为awaitable

promise_type

一个最简单的promise_type可能有如下的结构

struct promise_type
{
  auto get_return_object();
  auto initial_suspend();
  auto finial_suspend();
  void return_void();
  void unhandled_exception();
};

最简单的coroutine

有了前面的铺垫,现在就可以很轻松的写出第一个协程了

#include <coroutine>
#include <iostream>

struct co
{
  struct promise_type;
  std::coroutine_handle<promise_type> h;
  co(std::coroutine_handle<promise_type> h) : h{h} {}
  ~co() { h.destroy(); }
  struct promise_type
  {
    std::suspend_always initial_suspend() { return {}; }
    std::suspend_always final_suspend() { return {}; }
    co get_return_object()
    {
      return std::coroutine_handle<promise_type>::from_promise(*this);
    }
    void unhandled_exception() {}
  };

  void resume()
  {
    h.resume();
  }
};

co my_coroutine()
{
  std::cout << "enter\n";
  co_await std::suspend_always{};
  std::cout << "leave\n";
}

int main()
{
  auto c = my_coroutine();
  c.resume();
  std::cout << "end\n";
}

编译运行会发现”leave”没有打印,把 suspend_always改为suspend_never后就打印出来了,符合前面awaitable的描述。

生成器

非对称协程的代表,略。

网络库

前面提到过很多次了,协程就是为了减少线程调度才用的,相比有栈协程,C++的无栈协程其实就是个状态机,只需要极少的内存占用,函数调用的额外开销,用在网络编程可以进一步增加扩展性,Gor Nishanov同志把这叫做:a negative overhead abstraction。

代码见这里 https://github.com/abbycin/conet 贴张图好了

ksnip_20201024-182438.png

注意

建议观看

补一个完整点的