1 什么是協程?
協程(coroutine)是一種程序運行的方式,即在單線程里多個函數並發地執行.
A coroutine is a function that can suspend its execution (yield) until the given given YieldInstruction finishes.
個人簡單的理解是, 在協程中的函數, 如果執行過程中遇到了I/O密集型任務, 被給定的YieldInstruction
(讓出指令)暫停執行(yield), 交出執行權到其他函數, 直到被給定的YieldInstruction
(讓出指令)結束, 再繼續執行。
2 協程與線程的區別
- 由於協程的特性, 適合執行大量的I/O 密集型任務, 而線程在這方面弱於協程
- 協程涉及到函數的切換, 多線程涉及到線程的切換, 所以都有執行上下文, 但是協程不是被操作系統內核所管理, 而完全是由程序所控制(也就是在用戶態執行), 這樣帶來的好處就是性能得到了很大的提升, 不會像線程那樣需要在內核態進行上下文切換來消耗資源,因此協程的開銷遠遠小於線程的開銷
- 同一時間, 在多核處理器的環境下, 多個線程是可以並行的,但是運行的協程的函數卻只能有一個,其他的協程的函數都被suspend, 即協程是並發的
- 由於協程在同一個線程中, 所以不需要用來守衛臨界區段的同步性原語(primitive)比如互斥鎖、信號量等,並且不需要來自操作系統的支持
- 在協程之間的切換不需要涉及任何系統調用或任何阻塞調用
- 通常的線程是搶先式(即由操作系統分配執行權), 而協程是由程序分配執行權
3 協程的原理
當出現IO阻塞的時候,由協程的調度器進行調度,通過將數據流立刻yield掉(主動讓出),並且記錄當前棧上的數據,阻塞完后立刻再通過線程恢復棧,並把阻塞的結果放到這個線程上去跑,這樣看上去好像跟寫同步代碼沒有任何差別,這整個流程可以稱為coroutine,而跑在由coroutine
負責調度的線程稱為Fiber
。比如Golang里的 go關鍵字其實就是負責開啟一個Fiber
,讓func
邏輯跑在上面。
由於協程的暫停完全由程序控制,發生在用戶態上;而線程的阻塞狀態是由操作系統內核來進行切換,發生在內核態上。
因此,協程的開銷遠遠小於線程的開銷,也就沒有了ContextSwitch上的開銷。
4 協程的應用場景
協程的應用場景主要在於 :I/O 密集型任務。
這一點與多線程有些類似,但協程調用是在一個線程內進行的,是單線程,切換的開銷小,因此效率上略高於多線程;
當程序在執行 I/O 時操作時,CPU 是空閑的,此時可以充分利用 CPU 的時間片來處理其他任務;
在單線程中,一個函數調用,一般是從函數的第一行代碼開始執行,結束於 return 語句、異常或者函數執行(也可以認為是隱式地返回了 None );
有了協程,我們在函數的執行過程中,如果遇到了I/O密集型任務,函數可以臨時讓出控制權,讓 CPU 執行其他函數,等 I/O 操作執行完畢以后再收回其他函數的控制權.
參考來源
https://docs.unity3d.com/540/Documentation/ScriptReference/Coroutine.html
https://zh.wikipedia.org/wiki/協程
https://stackoverflow.com/questions/553704/what-is-a-coroutine