協程與例程

2021-06-20 06:12:02 字數 1814 閱讀 4262

協程(coroutine)技術是一種程式控制機制,早在上世紀60年代就已提出,用它可以很方便地實現協作式多工。

協程是一種程式元件,是由子例程的概念泛化而來的,子例程只有乙個入口點且只返回一次,而協程允許多個入口點,可以在指定位置掛起和恢復執行。

被廣為引用的協程定義:

•協程的本地資料在後續呼叫中始終保持

•協程在控制離開時暫停執行,當控制再次進入時只能從離開的位置繼續執行

在協程中,控制可以從當前執行上下文跳轉到程式的其它位置,並且可以在之後的任意時刻恢復當前執行上下文,控制從跳出點處繼續執行。這種行為與continuation類似,但協程相比之下更容易理解,某些情況下還更加高效。

熟悉c/c++語言的人都知道,乙個例程也就是乙個函式。當我們呼叫乙個函式時,執行流程進入函式;當函式執行完成後,執行流程返回給上層函式或例程。期間,每個函式執行共享乙個執行緒棧;函式返回後棧頂的內容自動**。這就是例程的特點,也是現代作業系統都支援這種例程方式。

協程與例程相對,從抽象的角度來說,例程只能進入一次並返回一次,而協程可能進入多次並返回多次。

void fun(int val)

如果上面的**是乙個例程,那麼它只能把 1、2、3 依次執行後,才返回。如果是協程,它可能在 1 處暫停,然後在某個時刻從 2 處繼續執行;接著在 2 處執行完之後暫停,然後在另外乙個時刻從 3 處繼續執行。

我們傳統上理解的函式,概念上也叫做子例程,是通過呼叫棧來傳遞呼叫關係的。協程則是比子例程更一般化的概念。子例程的呼叫是lifo,它的啟動位置是唯一入口,且只能返回一次;而協程允許有多個入口,且可以返回多次,你可以在特定的地方暫停和重新執行它。

上下文切換最早是指程序的上下文切換(context switch),它發生在核心態。核心排程器會對每個cpu上執行的程序進行排程(scheduling),以保證每個程序都能分到cpu時間片。當乙個程序的時間片用完,或被中斷後,核心將儲存該程序的執行狀態(即上下文),將其存入執行佇列(run queue),同時讓新的程序占用cpu。程序的上下文切換包括記憶體位址空間、核心態堆疊和硬體上下文(cpu暫存器)的切換,所以代價很高(具體參閱utlk程序一章)。

由於程序切換開銷大,所以設計了執行緒。linux 2.6核心的clone()系統呼叫已經支援建立核心級執行緒,且發布了核心執行緒庫pthread。在同一程序內的執行緒可以共享程序的位址空間,執行緒僅需要維護自己的暫存器、棧和執行緒相關的變數。不過核心執行緒的排程仍然需要由核心完成,這需要進行使用者態和核心態的模式切換,至少包括堆疊和記憶體對映的切換。而且,不同程序之間的執行緒切換,有可能會還會導致程序切換,所以代價還是不小。

為了進一步減小核心態執行緒上下文切換的開銷,於是又有了使用者態執行緒設計,即纖程(fiber)。纖程的排程可以由程式或執行緒庫來完成,這樣纖程上下文切換的開銷比核心級執行緒切換要小得多。乙個執行緒能擁有乙個或多個纖程(fiber)。這就是使用者執行緒的乙個實現。纖程間的排程都是在使用者空間內完成的。

執行緒的排程,通常是由作業系統的執行緒排程器完成,在現代os中,通常使用搶占式排程策略。而纖程的呼叫,完全依賴於程式設計師自己,即實現一種合作式排程,只有在主動提出切換時,才會進行切換。

要自己實現fiber,主要是儲存執行緒上下文。注意要儲存的資訊有:各個暫存器,棧頂和棧底,異常資訊,浮點暫存器。

程序、執行緒、協程的設計,都是為了併發任務能夠更好的利用cpu資源,協程可以作為程序和執行緒的有力補充。由於我們可以在使用者態排程協程任務,所以,我們可以把一組互相依賴的任務設計成協程。這樣,當乙個協程任務完成之後,可以手動進行任務排程,把自己掛起(yield),切換到另外乙個協程執行。這樣,由於我們可以控制程式主動讓出資源,很多情況下將不需要對資源加鎖。

總的看來,協程有三個好處:

python協程與非同步協程

在前面幾個部落格中我們一一對應解決了消費者消費的速度跟不上生產者,浪費我們大量的時間去等待的問題,在這裡,針對業務邏輯比較耗時間的問題,我們還有除了多程序之外更優的解決方式,那就是協程和非同步協程。在引入這個概念之前我們先看 看這個圖 從這個我們可以看出來,假如來了9個任務,即使我們開了多程序,在業...

協程巢狀協程

import asyncio import functools 第三層協程 async def test1 print 我是test1 await asyncio.sleep 1 print test1已經睡了1秒 await asyncio.sleep 3 print test1又睡了3秒 ret...

協程與執行緒

reference 協程不只在go語言中實現了,其實目前大部分語言都實現了自己的一套協程,包括c erlang python lua j ascript ruby等。相對於協程,你可能對程序和執行緒更為熟悉。程序一般代表乙個應用服務,在乙個應用服務中可以建立多個執行緒,而協程與程序 執行緒的概念不一...