C语言中的协程

来源:互联网 发布:mac同时登陆多个qq 编辑:程序博客网 时间:2024/06/03 17:05

引自:http://www.oschina.net/translate/coroutines-in-c

在《计算机程序设计艺术》中,Donald Knuth提供了一个解决这类问题的方法。他的方法是彻底丢掉堆栈的概念,不要再想一个进程作为调用者,另一个作为被调用者,把他们当做平等的协作者关系。

实际上就是:把传统的“调用”稍微改为一个不同的方式。新的“调用”将在某个地方保存返回值而不是堆栈上,并且还能跳转到另一个保存返回值的指定位置上。因此,解码器每次生成一个字符,就保存它的程序计数器并且跳转到上次解析器的位置-解析器每次都需要一个新的字符,它保存自己的程序计数器并且跳转到上次解码器的位置。程序可以在两个函数之间来回自如的传递需要的数据了。

理论上看起来很美,但实际中你却只能在汇编语言中使用,因为通用的高级语言没有一个支持调用原始的协程。像类似于C的都是依赖于基础的堆栈结构,因此当在函数间进行数据传递时,一个必须作为调用者,领一个必须作为被调用者。所以如果你想写可移植的代码,这种技术和Unix管道一样不切实际。

int fuction(void){  static int i, state=0;  switch(state){    case 0: goto LABEL0;    case 1: goto LABEL1;  }  LABEL0:  for(i=0; i<10; i++)  {    state = 1;    return i;  LABEL1:;  }}
更进一步:
int function(void){  static int i, state=0;  switch(state){  case 0:    for(i=0; i<10; i++){      state = 1;      retrn i;  case 1:;    }  }}
更进一步
#define crBegin static int state=0; switch(state){case 0:#define crReturn(i,x) do(state=i; return x; case i:;)while(0)#define crFinish }int function(void){  static int i;  crBegin;  for(i=0; i<10; i++)    crReturn(1,i);  crFinish;}

剩下的唯一问题是传给crReturn的第一个参数。就像在上一节引进一个新标签一样,我们必须避免与已存在的标签名冲突,确保所有给crReturn的状态参数都是不同的。这影响是相当小的 -- 编译器会抓住它并并不让它在运行时出错 -- 但我们还是要避免这样做。

虽然这可以解决,ANSI C还是提供了扩展到当前行号的专门的宏名:__LINE__,因此我们可以把crReturn重写成:

#define crReturn(x) do(state=__LINE__; return x;\case:__LINE__;)while(0)

0 0
原创粉丝点击