動態

詳情 返回 返回

協程必知必會-系列1-協程是什麼 - 動態 詳情

協程(Coroutine)是什麼?

協程就是用户態的線程。

這樣解釋可能過於抽象,讓我們先來回顧一下,另外2個更常見的概念,進程(Process)與線程(Thread)。

「進程是操作系統分配資源的基本單位」,只有在進程內才可以進行內存分配釋放、文件讀寫、網卡數據的接收與發送等的資源操作。

「線程是操作系統調度的基本單位」。

進程和線程的狀態對應用程序透明,並且在內核態中完成調度。

協程對應用程序來説是有狀態的,需要應用程序自行在用户態中完成協程的調度。

image.png

協程解決什麼問題?

其實協程這個概念很早就被提出來了,到了互聯網高速發展的階段,才被重視起來。

互聯網上但凡熱門的應用,都至少有成百萬、千萬甚至是上億的用户在使用,服務端同一時間需要處理大量用户的請求。

服務端需要具備處理高併發請求的能力,快速響應用户的請求。

互聯網的服務端基本上執行都是「IO密集型的任務」,而傳統的多進程、多線程併發模型並不能高效的利用cpu,它們在遇到IO阻塞的時候,當前的進程或者線程就會被掛起,併發處理請求的能力有限。

雖然可以使用「IO多路複用 + Reactor模型」來實現高併發,但是這種方案下,業務代碼中充斥着很多的「異步回調函數」,開發人員「心智負擔很重,代碼很難維護」。

引入協程後可以很好的解決這個問題,「IO阻塞時當前協程自動讓出執行權,等IO就緒時再恢復之前被掛起協程的執行」。這樣就可以在業務層採用「同步編碼」,而最後是「異步執行」的效果。在降低心智負擔的同時,也能提供高性能的服務。

協程該如何使用?

一個協程庫的實現,至少需要提供3個API,它們分別是:協程創建(CoroutineCreate)、協程喚醒(CoroutineResume),協程讓出(CoroutineYield)。

為了更好的讓大家掌握協程的概念,我自己使用C++11實現了一個協程庫,並把它開源在github上,地址鏈接為:https://github.com/wanmuc/MyCoroutine

現在讓我們來看看,該如何使用上面的協程庫,在協程中打印出”hello world“,示例代碼如下所示。

#include "mycoroutine.h"
#include <iostream>

using namespace std;
using namespace MyCoroutine;

void HelloWorld(Schedule &schedule) {
  cout << "hello ";
  schedule.CoroutineYield();
  cout << "world" << endl;
}

int main() {
  // 創建一個協程調度對象,並自動生成大小為1024的協程池
  Schedule schedule(1024);
  // 創建一個從協程,並手動調度
  int32_t cid = schedule.CoroutineCreate(HelloWorld, ref(schedule));
  schedule.CoroutineResume(cid);
  schedule.CoroutineResume(cid);
  return 0;
}

在上述代碼中,我們先創建了一個協程調度對象schedule,它會自動創建對應協程池。Schedule類的成員函數CoroutineCreate用於創建協程,成員函數CoroutineResume用於恢復協程的執行,成員函數CoroutineYield用於協程讓出執行權。

在main函數中,創建完協程之後,調用CoroutineResume來啓動協程的執行,協程的啓動HelloWorld被執行,打印完”hello “之後,協程主動調用CoroutineYield函數,讓出執行權。

進程回到main函數中執行,再次調用CoroutineResume函數,恢復協程的執行,協程從上一次被中斷的地方繼續執行,打印出"world",然後協程執行完畢並退出。

協程退出之後,進程回到main函數中繼續執行,main執行return語句,整個進程退出執行。

協程本質上是在一個進程中,「創建多個調用棧幀,並在不同的調用棧幀之間切換的執行」。在上面的例子中,main函數和HelloWorld函數就是兩個獨立的調用棧幀。

如果還不能很好理解整個調度過程,可以參考下圖。

image.png

本文為大廠後端技術專家萬木春原創文章。作者更多技術乾貨,見下方的書籍。

image.png

Add a new 評論

Some HTML is okay.