coroutine


第一次听说coroutine这东西是两年多前在Rust编程交流群1群里面,群里的钟宇腾在给Rust实现协程,并且总结了几个网络编程服务端的范式

  1. 单进程单线程
  2. 多进程
  3. 多线程
  4. Eventloop + 单线程
  5. Eventloop + 多worker进程(Nginx)
  6. 单Eventloop线程,多worker线程(线程池)
  7. One loop per thread + 线程池
  8. 单Eventloop + 多协程运行在线程池
  9. One loop per thread + 多协程 第9就是,开一个线程池,每个线程跑一个eventloop,然后事务都开协程来处理,协程不在线程间传递
  10. one loop per thread + 协程调度(可迁移)
  11. one loop + 协程调度
  12. half sync half async

某次,Linux吧的某位吧友提问有没有人了解ucontext这个东西,当时我在看TLPI的信号部分,误以为和sigaction有关(事实上是有来联系的),后来了解到协程这个东西,于是利用ucontext写了几个例子,感觉并不好用,也没有继续深入的了解。

再后来,学习Racket的过程中知道了call/cc这个东西,并且可以利用这个东西由用户自己实现上下文切换。在搜索call/cc相关材料时有发现了Mr.li的libgo,由于刚开始学习Racket,对call/cc理解起来有些困难,所以看了C++实现的libgo的源码,但是libgo并没有自己实现上下文切换,而是使用boost.context或者boost.coroutine进行了封装,并且hook了一些系统调用。

再后来,对协程失去了兴趣。上个月又看了下Python,其中yield这东西又让我想起了协程这个东西

def fib(n):
    first, second = 1, 1
    yield first
    yield second
    for i in range(1, n + 1):
        third = first + second
        first = second
        second = third
        yield third

if __name__ == '__main__':
    x = fib(8)
    for i in x:
        print(i, end = ' ')
    print()

上面的代码翻译成C++是这样

#include <iostream>
#include <boost/coroutine2/all.hpp>

using coro_t = boost::coroutines2::coroutine<int>;

auto main(int argc, char* argv[]) -> int
{
  if(argc != 2)
  {
    std::cerr << argv[0] << " number\n";
    return 1;
  }
  int n = std::stoi(argv[1]);
  coro_t::pull_type x([&](coro_t::push_type& yield) {
    int first = 1, second = 1;
    yield(first);
    yield(second);
    for(int i = 0; i < n; ++i)
    {
      int third = first + second;
      first = second;
      second = third;
      yield(third);
    }
  });

  for(int i: x)
    std::cout << i << ' ';
  std::cout << '\n';
}

那么,协程有哪些使用场景呢?

首先, 计算密集的任务,用协程是不明智的,应该用线程替代,充分利用多核的优势;I/O密集型任务,可以考虑使用协程,想象一个忙碌的web server,频繁的接受用户的请求并作出回复,由于页面不大且用户众多,使用多个线程来处理的话,频繁的进行线程调度就不好了,换成协程,单线程就能把带宽打满,用户自己切换状态,开销就会小很多。

wtf,我还是把上面用协程的范式实现一遍算了。。。



转载请注明:Serenity » coroutine

上一篇

下一篇