協程 vs 執行緒

2022-09-16 00:06:29 字數 2607 閱讀 4141

協程概念

「協程」(coroutine)概念最早由 melvin conway 於 1958 年提出。雖然被提出的時間很早,但是使用它的年限很短。尤其是最近幾年,隨著 go、lua 等語言的流行,把協程推向了乙個新的高潮。

協程其實可以認為是比執行緒更小的執行單元。為啥說他是乙個執行單元,因為他自帶cpu上下文。這樣只要在合適的時機,我們可以把乙個協程 切換到 另乙個協程。只要這個過程中儲存或恢復 cpu上下文那麼程式還是可以執行的。

協程可以理解為純使用者態的執行緒,其通過協作而不是搶占來進行切換。相對於程序或者執行緒,協程所有的操作都可以在使用者態完成,建立和切換的消耗更低。總的來說,協程為協同任務提供了一種執行時抽象,這種抽象非常適合於協同多任務排程和資料流處理。在現代作業系統和程式語言中,因為使用者態執行緒切換代價比核心態執行緒小,協程成為了一種輕量級的多任務模型。

從程式設計角度上看,協程的思想本質上就是控制流的主動讓出(yield)和恢復(resume)機制,迭代器常被用來實現協程,所以大部分的語言實現的協程中都有 yield 關鍵字,比如 python、lua。但也有特殊比如 go 就使用的是通道來通訊。先來張圖看看:

協程和執行緒差異

那麼這個過程看起來比執行緒差不多哇。其實不然 執行緒切換從系統層面遠不止 儲存和恢復 cpu上下文這麼簡單。作業系統為了程式執行的高效性每個執行緒都有自己快取cache等等資料,作業系統還會幫你做這些資料的恢復操作。所以執行緒的切換非常耗效能。但是協程的切換只是單純的操作cpu的上下文,所以一秒鐘切換個上百萬次系統都抗的住。

協程的問題

但是協程有乙個問題,就是系統並不感知,所以作業系統不會幫你做切換。那麼誰來幫你做切換?讓需要執行的協程更多的獲得cpu時間才是問題的關鍵。

協程目前主流設計

目前的協程框架一般都是設計成 1:n 模式。所謂 1:n 就是乙個執行緒作為乙個容器裡面放置多個協程。那麼誰來適時的切換這些協程?答案是有協程自己主動讓出cpu,也就是每個協程池裡面有乙個排程器,這個排程器是被動排程的。意思就是他不會主動排程。而且當乙個協程發現自己執行不下去了(比如非同步等待網路的資料回來,但是當前還沒有資料到),這個時候就可以由這個協程通知排程器,這個時候執行到排程器的**,排程器根據事先設計好的排程演算法找到當前最需要cpu的協程。切換這個協程的cpu上下文把cpu的執行權交個這個協程,直到這個協程出現執行不下去需要等等的情況,或者它呼叫主動讓出cpu的api之類,觸發下一次排程。對的沒錯就是類似於 領導人模式

這個設計有問題嗎?

其實是有問題的,假設這個執行緒中有乙個協程是cpu密集型的且沒有io操作,也就是自己不會主動觸發排程器排程的過程,那麼就會出現其他協程得不到執行的情況,所以這種情況下需要程式設計師自己避免。這是乙個問題,假設業務開發的人員並不懂這個原理的話就可能會出現問題。

協程舉例理解

在所有語言中都存在著層級呼叫,比如 a 呼叫 b,b 在執行過程中又呼叫了 c,c 執行完畢返回,b 執行完畢返回,最後是 a 執行完畢。這種方法、函式、子程式的呼叫方式都是通過 棧 實現的,乙個執行緒就是執行乙個子程式。子程式呼叫總是乙個入口,一次返回,呼叫順序是明確的。而協程的呼叫和子程式不同。協程看上去也是子程式,但執行過程中,在子程式內部可中斷,然後轉而執行別的子程式,在適當的時候再返回來接著執行。

public

void

coroutinea()

public

void

coroutineb()

上面**如果由協程來執行,那麼在執行 coroutinea 的過程中,可以隨時中斷,去執行 coroutineb,coroutineb 也可能在執行過程中中斷再去執行 coroutinea,所以,最終的結果可能是:

1ab2c3

但是,在上面的**中,並沒有在 coroutinea 方法中呼叫 coroutineb。執行結果就像兩個執行緒在併發執行。但其實,通過協程執行用的是乙個執行緒,只不過這個執行緒看起來有點「到處亂跑」。

協程優勢

協程 vs 執行緒 比較有以下 3 個重要的優勢:

1、減少了執行緒切換的成本。執行緒,不管是建立還是切換,都需要較高的成本。子程式切換不是執行緒切換,而是由程式自身控制,因此,沒有執行緒切換的開銷,和多執行緒比,執行緒數量越多,協程的效能優勢就越明顯。這也就是說,協程的效率比較高

2、協程的第二大優勢就是沒有併發問題,不需要多執行緒的鎖機制,因為只有乙個執行緒,也不存在同時寫變數衝突,在協程中控制共享資源不加鎖,只需要判斷狀態就好了,所以執行效率比多執行緒高很多

3、協程更輕量級。建立乙個執行緒棧大概需要 1m 左右,而協程棧大概只需要幾 k 或者幾十 k。

有優勢也有劣勢,因為前面的程式看起來在「上串下跳」,所以,協程看起來沒那麼好控制。

協程認知注意

1、對於作業系統來說只有程序和執行緒,協程的控制由應用程式顯式排程,非搶占式的

2、協程的執行最終靠的還是執行緒,應用程式來排程協程選擇合適的執行緒來獲取執行權

3、協程適合於 io 密集型場景,這樣能提高併發性,比如請求介面、mysql、redis 等的操作

4、協程並不是說替換非同步,協程一樣可以利用非同步實現高併發。

5、協程要利用多核優勢就需要比如通過排程器來實現多協程在多執行緒上執行,這時也就具有了並行的特性。如果多協程執行在單執行緒或單程序上也就只能說具有併發特性。

python 執行緒,協程

本文 threading用於提供執行緒相關的操作。執行緒是應用程式中工作的最小單元,它被包含在程序之中,是程序中的實際運作單位。一條執行緒指的是程序中乙個單一順序的控制流,乙個程序中可以併發多個執行緒,每條執行緒並行執行不同的任務。threading 模組建立在 thread 模組之上。thread...

程序 執行緒 協程

多程序多執行緒的最終目地都是為了加快任務處理的時間,但是受限於cpu核數 只有多核才能實現並行,任務耗時 單核任務耗時 cpu核數 那麼在單核上執行多程序多執行緒是不是沒有用了,加快不了處理的速度了?答案肯定是不是的,不然這個東西設計出來太雞肋了,那它們加快處理的原理是什麼呢?乙個程序耗時 b程序耗...

程序 執行緒 協程

程序是系統資源分配的最小單位,系統由乙個個程序 程式 組成。一般情況下,包括文字區域 text region 資料區域 data region 和堆疊 stack region 檔案描述符表 程序每次開啟乙個檔案,系統就會在該程序的使用者檔案描述符表中分配乙個相應的表項,表項的索引返回給該程序,用於...