今天看了云风的blog里面写到了一个片段:“我们采用的是大量进程(非 OS进程,这里指 Erlang进程)协作工作的模式。”大体思考了一下,他们用c+lua开发,这里指的Erlang进程,大概指的是采用了与Erlang相似的“协程”方式。于是顺便了解了一下协程的概念和最简单的实现。
coroutines是一个古老的计算模式,中文翻译叫“协程”,在现代程序语言里面属于“非主流”,erlang语言底层就是采用的这种模式实现的。
coroutine和subroutine(子程)的不同点在于coroutine可以很多个调用入口点,而subroutine只能有一个。当coroutine被第一次调用到的时候,它将从起始处开始执行,一旦遇到具有yield(类似subroutine中的return语句)语义的语句的时候,就是返回给另外一个coroutine或者调用者,而在接下来再次被调用的时候,就从yield语句下面的一条语句继续执行直到遇到新的yield语句或者coroutine的结束。
对于subroutine调用方式已根深蒂固的开发者说,上面的coroutine介绍肯定是非常抽象和空洞的,下面就通一个使用C语言实现的一个非常简单的coroutine程序来具体化这个概念:
注:最开始读这种方式比较吃力,进行跟踪之后才大体理清楚了执行过程,读懂这些有助于了解“协程”这一概念。
#include <iostream>
using namespace std;
intswitch_magic(void){
static int i, state =0;
cout<<"State:"<<state<<"i:"<<i<<endl;
switch(state){
case 0: // start offunction
for(i = 0; i < 10; i++){
state= 1; // so we will come back to "case 1"
cout<<"I:"<<i<<endl;
return i; // resume controlstraight after the return
case 1:
cout<<"what?"<<endl;
}
}
return -1;
}
int main(){
cout<<switch_magic()<<endl;
cout<<switch_magic()<<endl;
cout<<switch_magic()<<endl;
cout<<switch_magic()<<endl;
cout<<switch_magic()<<endl;
cout<<switch_magic()<<endl;
cout<<switch_magic()<<endl;
cout<<switch_magic()<<endl;
cout<<switch_magic()<<endl;
cout<<switch_magic()<<endl;
cout<<switch_magic()<<endl;
getchar();
return 0;
}
运行输出:
0
1
2
3
4
5
6
7
8
9
-1
![](https://img-my.csdn.net/uploads/201206/20/1340197843_8258.png)
通过定义一些精巧的macro可以屏蔽coroutine的C实现细节,让代码更加简单、易读:
#include <iostream>
using namespace std;
#define crBegin static int state=0;switch(state) { case 0:
#define crReturn(x) do { state=__LINE__; return x; \
case__LINE__:; }while (0)
#define crFinish }
int switch_magic(void) {
static int i;
crBegin
for (i = 0; i < 10; i++)
crReturn(i);
crFinish
return -1;
}
int main(){
cout<< switch_magic() << endl;
cout<< switch_magic() << endl;
cout<< switch_magic() << endl;
cout<< switch_magic() << endl;
cout<< switch_magic() << endl;
cout<< switch_magic() << endl;
cout<< switch_magic() << endl;
cout<< switch_magic() << endl;
cout<< switch_magic() << endl;
cout<< switch_magic() << endl;
cout<< switch_magic() << endl;
return 0;
}