以前一听人说起Coroutins这个概念,都感觉是很高大上。最近看了 Coroutins in C 这篇文章。 基本上搞清楚它是个什么东西了。
考虑一个生产者和一个消费者的情况。正常情况下,可以把它们分别放入两个线程或者进程中,生产者可以通过管道,把生产的数据写入管道,消费者从管道中取出数据来处理。
/* They are running in different thread or process */ int producer () { /* produce data and send to pipe */ } int consumer () { /* read from pipe and handle the data */ }
这样的办法,在一些情况下显得太“重”了,也不是可移植的。对于一些简单情况,我们也不太想为这么简单的问题用多线程。
协程,就是为了满足这个需求来的。
我们可以修改生产者的实现,每次调用生产者的时候,直接返回需要处理的数据,这样消费者就可以直接循环调用生产者。
int consumer () { while (data = producer()) { /* handle the data */ } } int producer () { /* return the right data each time it is called */ }
也可以修改消费者,每次都把需要处理的数据以参数的形式提供给它。这样的话,消费者可以每次以需要处理的数据为形参直接调用生产者。
int producer () { while (data) { consumer (data); } } int consumer (data) { /* handle the data and return. */ }
这两种修改方法,只需要改一种就可以了。无论改成哪一种,看起来我们现在也需发一个线程就可以了。 不过,这两种改法,都需要解决如果保存函数的上下文的问题。 简单来说,改法一,每次调用producer,产生的数据应该是不同的,直到最后没有数据产出。 改法二,每次消费者处理了数据后,应该保存其状态,因为很多时候后面的数据依赖于前面数据的处理结果,比如累加。
不同的人对这个问题有不同的解法,不同的语言应该也有不同的实现,英文原文把C的实现讲得很清楚。这里拷一个改生产者的C代码过来:
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 */ } }
这个例子,循环调用这个function,可以顺序产生0到9的数。是用静态变量和goto来做的。
后面还有用switch来实现的宏。具体请参考原文。
如果你觉得本文不错,欢迎 donate