以前一听人说起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