天天看點

快速上手,協程剖析

快速上手,協程剖析

協程也叫微線程,英文名稱為coroutine。一個程序可以有多個線程,一個線程可以有多個協程,這是協程和線程間的關系。不同的是,線程由系統排程,但協程需要自己排程,協程運作在使用者态。

Linux核心為協程程式設計提供了支援,相關的函數聲明在ucontext.h頭檔案中。也可以借助longjmp、setjmp、pthread_attr_setstackaddr等組合實作,但複雜很多,ucontext提供的函數已幫助做了很多工作。要實作協程的并發(線程内的,顯然是假并發,實際還是串行的),要求主動調用swapcontext進行切換。

協程程式設計實際就是使用者态排程函數的執行次數,讓單個線程,看起來像是多線程。基于它可以實作僞同步,也就是将異步變成同步調用。

協程的原理非常簡單,假設任務A劃分成A1、A2、A3三個子任務,任務B劃分成任務B1和B2兩個子任務。利用協程,讓A和B可以并行進行,比如完成A1後,立即執行B1,B1完成後執行A2,A2完成後執行B2,B2完成後執行A3。

為達到這個目的,在執行A1時,A1結束前需要調用swapcontext切換到B1。同理B1完成時,也需要調用swapcontext切換到A2。下面這個示例可以直接編譯執行,通過它可以體會到協程的效果。

// 協程示例

// 編譯: g++ -g -o x x.cpp

#include 

#include  // 協程相關api所在頭檔案

// 定義3個協程,類似于3個線程

static void foo();

static void woo();

static void zoo();

static ucontext_t ctx1; // 協程zoo的上下文,由makecontext調用構造

static ucontext_t ctx2; // 協程woo的上下文,由makecontext調用構造

static ucontext_t ctx3; // 協程foo的上下文,由swapcontext自動構造

// stack為new/malloc出來的也可以的

static char stack1[4096]; // 協程zoo的棧,得合适大小,否則一樣會出現棧溢出

static char stack2[8192]; // 協程woo的棧,得合适大小,否則一樣會出現棧溢出

int main()

{

printf("main 1\n");

    // 構造協程zoo的上下文

getcontext(&ctx1);

ctx1.uc_stack.ss_sp = stack1;

ctx1.uc_stack.ss_size = sizeof(stack1);

ctx1.uc_link = &ctx3; // 在ctx1之後的上下文

makecontext(&ctx1, zoo, 0); // 在運作完zoo之後,運作ctx3

    // 構造協程woo的上下文

getcontext(&ctx2);

ctx2.uc_stack.ss_sp = stack2;

ctx2.uc_stack.ss_size = sizeof(stack2);

ctx2.uc_link = &ctx1;

makecontext(&ctx2, woo, 0); // 在運作完zoo之後,運作ctx1

foo();

printf("main 2\n");

return 0;

}

// 可把foo當成一個線程,不過它是微線程

void foo()

printf("%s 1\n", __func__);

    // 切換到ctx2執行,也就是執行woo,目前的儲存在ctx3

swapcontext(&ctx3, &ctx2);

    // 當切回到ctx3時,會執行以下代碼段

printf("%s 2\n", __func__);

// 也可把woo當成一個線程,不過它是微線程

void woo()

printf("%s\n", __func__);

// 也可把zoo當成一個線程,不過它是微線程

void zoo()

繼續閱讀