# 協程 > 線程分為用戶級線程,內核級線程和輕量級線程。Linux中使用的是輕量級線程,而協程雖然是運行線上程之上,但是是run在用戶空間。並且協程和線程一樣,擁有自己的調度器、cpu的上下文切換等。 > > 協程在我個人看來是一種用戶級線程; > > - 這是因為對於cpu有上下文的切換,而且是 ...
協程
線程分為用戶級線程,內核級線程和輕量級線程。Linux中使用的是輕量級線程,而協程雖然是運行線上程之上,但是是run在用戶空間。並且協程和線程一樣,擁有自己的調度器、cpu的上下文切換等。
協程在我個人看來是一種用戶級線程;
- 這是因為對於cpu有上下文的切換,而且是在用戶空間的層次進行數據處理;一旦被內核的代碼阻塞,是無法進行解除阻塞狀態;
協程的功能
首先需要明確一點,協程是幹嘛的,針對的事物是什麼?
- 協程針對的是IO處理,可以將多個同步關係的IO處理的性能接近於非同步IO的效率;
- 這樣既保證了編寫代碼的邏輯,又保證了代碼執行的效率;
非同步IO的效率高,是正常的,因為調用某個IO函數,也就是系統調用,根據非同步IO模型中的描述,它不會被阻塞且處理完畢後會通知調用線程,也就是非同步IO模型沒有阻塞時間。
協程的做法
那麼問題就來了,應該是一個什麼樣的邏輯處理同步IO,才能讓其性能接近非同步IO呢?
- 消除同步IO被阻塞住時的等待時間,從而讓效率無限接近於非同步IO。
- 這就是協程乾的功能,一旦被阻塞住了,就會把cpu讓出給其他可以執行的協程。
cpu切換上下文過程
協程由運行體和調度器組成。
協程的運行體中保存著讓出後的寄存器狀態,方便於之後恢復、子過程函數和其參數、自身協程的狀態、棧的大小等;
也就引出了yeild和resume這兩個功能
- yeild的中文名叫讓出,cpu每個時刻只能運行一個操作(),讓出操作會讓當前cpu的寄存器空出給其他協程運行體(函數)使用
- resume的中文名叫恢復,cpu空出後,恢復之前協程運行體(函數)執行的位置。
由於討論的是一個線程的操作,不會出現內核切換線程的操作。
接下來要說是調度器,使用yeild和resume兩個操作,進行切換協程。使用協程A->調度器->協程B這種形式的切換,而不是協程A->協程B。下圖可以很好說明這個形式。
協程狀態檢測
協程調度器在調度協程運行體的時候就需要維護協程所具有的狀態,比如就緒、等待、睡眠。
這也就需要一些數據結構進行維護這些運行體。
- 就緒狀態由於這些運行體不需要設置優先順序,就可以使用隊列先進先出的性質;
- 睡眠一定會有過期時間,就可以使用定時器相關的數據結構,比如紅黑樹、最小堆等。
- 等待IO準備也是有時間的,同睡眠狀態一樣,使用相同的數據結構
總結
- 協程是針對同步IO處理的一個組件,讓同步邏輯的代碼有著非同步的效率;
- 利用IO阻塞的時間,去處理其他的事情。但是你要說它是非同步也不對,它的整體代碼邏輯是串列的;
- 協程是由運行體和調度器組成;
- 運行在一個線程上,針對於伺服器開發,不需要為每一個客戶端連接的IO創建一個線程(這種方法本身就是不對的,因為有上萬個客戶端,不能也創建上萬個線程,對吧),讓這個度比使用線程更小。
擴展
至於它和reactor進行對比,它倆的性能差距不會很大。
協程相比於reactor,reactor的回調函數太過於分散,不易代碼可讀。
reactor其實是對事件進行一個非同步的處理,於此同時也不需要看到IO處理的邏輯,只需要關註每一個事件應該怎麼做。