协程简单实现

协程(coroutine)是为协作式多任务编程设计的一种用于产生通用子例程(subroutine)的程序组件. 按照 Donald Knuth 的说法, “子例程是协程的特例”. 区别在于子例程只有一个入口和结束点, 而协程自第一次返回后再调用便从上一次返回点继续执行, 也就是说协程可以保持多次调用之间的状态. 协程是区别于线程(thread)的, 协程只是一种逻辑上多任务同时进行, 但其实多个相关协程是在同一条线程中的, 并且相较于线程, 协程的执行是有序的. 协程可以方便的实现协作任务、Actor Model、异常、事件循环、无限队列、管道等. 协程对于充分利用 CPU 是有很大的帮助的, 并且编程方面也比多线程有了极大的简化.

协程分为对称(symmetric)和非对称(asymmetric), 对称协程是通过 yeild 平级的调用另外一个协程, 形如生产者-消费者模型中的, 而非对称就类似于子例程调用是调用者-被调用者(caller-callee)关系, 这方面著名的例子是 Lua 协程.

协程的实现需要用到一个有别于子例程调用的栈的地方来保存调用状态, 这就引出了延续(Continuation) 的概念. 所谓延续就是程序执行状态保存地, 程序通过在某一点将执行状态保存在延续中, 并在以后通过读取延续而返回此点. 这类似于 CPU 的上下文切换. 这里的简单实现是通过 static 变量来保存程序状态的. 通过在每个 return 语句之后定义一个数字标签, 在返回前将这个数字保存在状态变量中, 并在下一次调用时读取这个状态变量, 并跳转到这个数字标签从而实现简单的协程. 代码如下:

int function(void) {
  static int i, state = 0;
  switch (state) {
    case 0: goto LABEL0;
    case 1: goto LABEL1;
  }
  LABEL0: /* start of function */
  for (i = 0; i < 10; i++) {
    state = 1; /* so we will come back to LABEL1 */
    return i;
    LABEL1:; /* resume control straight after the return */
  }
}

协程基本的原理便是如此. 如果希望得到更加详细的介绍推荐你看: 一个“蝇量级” C 语言协程库Coroutines in C

Leave a Reply

Your email address will not be published. Required fields are marked *