本帖最后由 nemon 于 2013-3-18 09:24 编辑
from:http://www.hawkwithwind.net/blog/2011/02/18/%E5%8D%8F%E7%A8%8B%E7%9A%84c%E5%AE%9E%E7%8E%B0/
auth:A WING BY WIND
今天正好跟ownwaterloo聊到协程,于是查了查资料,顺便写个博客记录一下吧。 协程的思想主要是为了解决多个任务如何分享CPU这个问题的。线程在很多协作问题上处理的不好,而且需要锁机制,导致运行缓慢,不易理解,容易出错。协程的思想是,一系列互相依赖的协程间依次使用CPU,每次只有一个协程工作,而其他协程处于休眠状态。与线程不同,协程是自己主动让出CPU,并交付他期望的下一个协程运行,而不是在任何时候都有可能被系统调度打断。因此协程的使用更加清晰易懂,并且多数情况下不需要锁机制(或者说他本身就是一个全局锁)。 Simon的举例是一个生产者消费者例子。传统的程序可能是生产者一个循环不断产生字符,之后退出。而消费者一个循环不断读取字符,并处理之。使用时或许会用一个管道,将生产者的输出重定向到消费者的输入。从而使两者协作。 Simon提出,如何使这样的简单任务无需通过管道这类操作系统相关的重型机制,就能完成。他提出,生产者或者消费者之间,有一方需要改变自己的工作模式。不再是在循环中不断处理任务,而是一次只处理一个字符然后立刻返回。而另一方依旧保持原状,只需在原本输出到或读取自标准IO的地方修改为调用对方的那个函数就可以了。 但这种写法难以维护。他提出了协程的概念,并期待写出类似这样的代码: - int function(void) {
- int i;
- for (i = 0; i < 10; i++)
- return i; /* won't work, but wouldn't it be nice */
- }
复制代码在这里return语句并不是终止函数,而是将函数转入睡眠的状态,并将CPU交付给另一个函数执行。例如生产者写入一个字符后,调用类似的return语句,交付消费者处理这个字符。稍后当消费者处理完并调用return语句后,能重新激活生产者协程,并继续这个for循环。 如何在C语言中实现这样的功能而不需使用汇编代码去hack堆栈和寄存器呢?他给出的最初的实现是使用goto语句。 - 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 */
- }
- }
复制代码巧妙的使用静态变量存储函数状态,并使用goto语句来继续for循环。但这种写法不够优美,于是又引入了Duff’s device。 - switch (count % 8) {
- case 0: do { *to = *from++;
- case 7: *to = *from++;
- case 6: *to = *from++;
- case 5: *to = *from++;
- case 4: *to = *from++;
- case 3: *to = *from++;
- case 2: *to = *from++;
- case 1: *to = *from++;
- } while ((count -= 8) > 0);
- }
复制代码使用这种trick来将switch-case语句作为循环中的跳转。这就避免了goto-label语句需要同时维护goto和label两处代码的麻烦。于是前面的代码可以变成这个样子。 - int function(void) {
- static int i, state = 0;
- switch (state) {
- case 0: /* start of function */
- for (i = 0; i < 10; i++) {
- state = 1; /* so we will come back to "case 1" */
- return i;
- case 1:; /* resume control straight after the return */
- }
- }
- }
复制代码他的文章后面还有一些内容,例如如何使用宏包装这套机制。如何使用__LINE__宏避免state被赋予相同的数值。如何避免多线程调用下干扰静态变量等等。我这里就不赘述了,大家有兴趣可参考原文。
总之读此文的两个收获一个是认识协程,一个是学习到了一种诡谲的C语言用法。非常开心。
|