1. 程式人生 > 實用技巧 >基於Duff's Device的C簡易無棧協程實現

基於Duff's Device的C簡易無棧協程實現

參考 Simon Tatham 的文章https://www.chiark.greenend.org.uk/~sgtatham/coroutines.html

譯文為https://mthli.xyz/coroutines-in-c/

協程是一組序列化的子過程,與執行緒不同,協程的排程是由使用者而非作業系統執行的,協程可以在任意時刻讓出CPU(稱為yield操作),下次呼叫時從上次yield的地方繼續執行

Simon Tatham利用Duff's Device實現了一種簡易的無棧協程,示例程式碼如下:

int function(void) {
    static int i, state = 0;
    switch (state) {
        case 0: 
        for (i = 0; i < 10; i++) {
            state = 1; 
            return i;
            case 1:;
        }
    }
} 

該函式的作用是第i次呼叫時返回i,最多10次(相同的函式功能可以通過一個statci變數實現,但本文是為了討論協程),其核心部分為switch語句以及return前後兩句,通過設定不同的state來保證下一次呼叫時從上次退出的地方繼續執行(如果不明白switch為何能和for套在一起寫可以自行搜尋Duff's Device)

可以看出,每次呼叫return時設定的state必須不同,可以利用__LINE__巨集來設定state,這樣只要不在一行呼叫兩次return即可

以下是將其用巨集封裝的程式碼

#include <stdio.h>

#define crBegin static int state = 0; switch (state) { case 0:
#define crReturn(x) do { state = __LINE__; return x; case __LINE__:; } while (0)
#define crFinish }

void f1() {
    crBegin;
    puts("1");
    puts("2");
    crReturn();
    puts("3");
    crFinish;
}

void f2() {
    crBegin;
    puts("x");
    crReturn();
    puts("y");
    puts("z");
    crFinish;
}

int main (void) {
    f1();
    f2();
    f1();
    f2();
    return 0;
} 

do while(0)是為了讓crReturn與if else巢狀時不用考慮大括號問題

這份程式碼執行後會依次輸出1,2,x,3,y,z

至此,一份用C實現的簡易無棧協程就完成了,需要注意的是,因為switch內部不能任意定義變數,請在crBegin之前定義所需變數

PS:這份程式碼僅供學習參考,如果你在實際生產程式碼中用了可能會被上司打死