![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIn5GcuYTM1gDOwMTNjVWNzcTYxQjZhNWMlRGOmJjYmRDZ1ITZfdWbp9CXt92Yu4GZjlGbh5SZslmZxl3Lc9CX6MHc0RHaiojIsJye.png)
協程也叫微線程,英文名稱為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()