程式、執行緒、協程到底是個什麼東西?

柘一發表於2018-08-23

一、前言

​ 不管是上大學課程《作業系統》,還是找工作面試,還是日常工作大家交流中,都離不開程式和執行緒,有些同學可能還會聽說過協程。那他們到底是什麼呢?他們之間有什麼關係呢?跟著我一起往下看吧!

二、計算機組成及作業系統

​ 在講程式之前,我們先回顧一下計算機的組成。

  1. 中央處理器(CPU):是計算機的核心,負責一些運算和控制

    CPU有兩大核心部件,ALU(算數邏輯單元)和CU(邏輯控制單元),其中ALU負責一些運算,包括算數運算(加減乘除)、邏輯運算(與或非)及關係運算(大小等於);CU負責各個邏輯部件的協調工作,充當一個指揮官的角色。CPU中也有暫存器,只不過容量極小,負責快取一些計算的中間結果。

  2. 主儲存器:儲存資料

    分為RAM和ROM。RAM,隨機儲存器,掉電資料丟失,就是俗稱的記憶體;ROM,只讀儲存器,掉電資料不丟失,就是俗稱的磁碟。

  3. IO:各種輸入輸出裝置(滑鼠、鍵盤、顯示器、網路卡、音效卡、顯示卡等)

有了這些計算機的硬體支援,我們就可以做各種各樣的事情了。如果我們手動來控制CPU和記憶體,那我們可能要寫各種底層指令,而且肯定會出現各種各樣的bug。作業系統在計算機硬體的基礎之上,封裝了硬體的實現細節,對上層抽象了一層更方便的系統呼叫指令,這就是作業系統。

在多工系統中,作業系統接管了所有硬體資源並持有對硬體控制的最高許可權。在作業系統中執行的程式,都以程式的方式執行在更低的許可權中。所有的硬體資源,由作業系統根據程式的優先順序以及程式的執行狀況進行統一的調配。

三、程式

​ 有了作業系統,我們就能夠在上面實現程式了。比如通過一個QQ來實現聊天功能。那QQ程式怎麼啟動起來呢,作業系統把QQ程式丟在一個容器裡並把它執行起來,而這個容器就是程式。那程式跟程式之間又是什麼關係呢?

  • 程式是死的(只是在磁碟中的一堆指令程式碼),程式是活的(會佔用CPU、記憶體、檔案資源、IO等)
  • 程式是程式的執行例項

四、執行緒

維基百科:執行緒是作業系統能夠進行運算排程的最小單位。它被包含在程式之中,是程式中的實際運作單位。一條執行緒指的是程式中一個單一順序的控制流,一個程式中可以併發多個執行緒,每條執行緒並行執行不同的任務。

一個程式有多個執行緒,拿QQ為例子,需要有一個執行緒監聽鍵盤的輸入並轉換為文字,需要有一個執行緒負責拉取對方發來的訊息等。從作業系統的角度來看:

  • 程式是最小的資源管理分配單元
  • 執行緒是最小的執行單元

無論是程式還是執行緒都是需要作業系統來控制的。拿執行緒來舉例,執行緒有多個執行狀態:初始化、可執行、執行中、阻塞、銷燬 五種狀態。這五種狀態的轉化關係如下:

執行緒狀態轉化圖

執行緒的狀態轉化是由作業系統核心中的TCB(thread control block)執行緒控制塊來改變的,需要耗費一定的CPU資源。

總結一下,程式是一個程式的執行例項,它管理著各種資源;一個程式有多個執行緒,執行緒才是具體的執行單元,他們共享著程式中的部分資源,同樣有著私有資源(PC程式計數器、執行棧等),執行緒間預設通過共享記憶體進行互動,執行緒間同步通過鎖/訊號量等進行互斥操作。由於執行緒切換需要在作業系統在核心/使用者態間的切換才能改變狀態,導致執行緒切換本身會非常耗費資源。

五、協程

​ 協程,又稱微執行緒,纖程。英文名Coroutine。比執行緒更加輕量級,就像一個程式有多個執行緒一樣,一個執行緒可以有多個協程。

舉一個廖雪峰老師給的例子:

def a():
	print 1
	print 2
def b():
	print 'A'
	print 'B'
複製程式碼

當我們呼叫a、b這兩個函式時,肯定會先後列印1 2 A B,如果我想列印1 A B 2 或者 1 A 2 B呢? 顯然通過多執行緒時不能控制的。這樣我們就可以通過協程來處理。通過程式自身的邏輯來實現流程跳轉,相比於執行緒中斷,有點像CPU中斷,不需要通過作業系統的介入,效率極高。

python語言本身支援協程,通過yield關鍵字來實現。

def a():
    print 1
    yield
    print 2
    yield
def b():
    print 'A'
    print 'B'
generator=a()
next(generator)
b()
generator.send('')
複製程式碼

該語句就會列印:

1
A
B
2
複製程式碼
  1. 通過呼叫a()生成一個執行器
  2. 通過next觸發執行器執行a並列印1
  3. 呼叫b列印AB
  4. 通過send像執行器傳送訊號繼續執行並列印2

整個流程是由一個執行緒執行,通過一些語法來控制執行流程,不涉及到鎖,不涉及到執行緒切換,是通過關鍵字來輔助ab協作工作,所以叫做協程

ES6中引入了for..of..,它本身是一個語法糖,原理是通過引入迭代器(iterator)來實現,而迭代器就是通過協程來實現的。而在ES7中引入了更加直觀的async/await`。

java本身不支援協程,但可以通過三方庫來實現,例如QuasarAkka等。

下一篇我們通過jsjava不同的實現來進一步瞭解協程。

參考文獻:

  1. wiki 程式
  2. 阮一峰 <<程式與執行緒的一個簡單解釋>>
  3. 漫畫:什麼是協程?
  4. 瞭解協程
  5. 廖雪峰 協程
  6. 協程與事件迴圈

相關文章