對於協程(coroutine),你必須知道事情都在這裡了(內附代碼)


對於協程(coroutine),你必須知道事情都在這裡了(內附代碼)

什麼是協程

協程(coroutine)的概念根據Donald Knuth的說法早在1958年就由Melvin Conway提出了,對應wikipedia的定義如下:

Coroutines are computer program components that generalize subroutines for non-preemptive multitasking, by allowing execution to be suspended and resumed. Coroutines are well-suited for implementing familiar program components such as cooperative tasks, exceptions, event loops, iterators, infinite lists and pipes.

這裡子例程(subroutine)是一個概括性的術語,子例程可以是整個程序中的一個代碼區塊,當它被主程序調用的時候就會進入運行。例如函數就是子例程中的一種。


對於協程(coroutine),你必須知道事情都在這裡了(內附代碼)

從wikipedia定義可以看出協程相比子例程更加的靈活,允許執行過程中被掛起和恢復,多個協程可以一起相互協作執行任務。從協程(co + routine)名字上來拆解為支持協作(cooperate)的例程。

協程與子例程的執行區別


對於協程(coroutine),你必須知道事情都在這裡了(內附代碼)

圖中左邊是子例程的執行過程,右邊是協程的執行過程,可以很明顯的看出來執行過程中的區別。

  • 先說左邊,子例程可以看成是某個函數,子例程的執行過程中通常是嵌套順序執行的過程,子例程1和子例程2的關係(調用和被調用)不是完全平等的,子例程1能調用子例程2,但子例程2不能反過來調用子例程1。
  • 再說右邊,協程1和協程2的關係是完全對等的,協程1執行過程中可以中斷掛起執行另外一個協程2,反之也是可以的,直到最終兩個協程都執行完以後再返回回到主程序中,即協程1和協程2相互協作完成了整個任務。

接下來舉一個協程實現生產者和消費者的例子:


對於協程(coroutine),你必須知道事情都在這裡了(內附代碼)

這裡有一個隊列queue,一個生產者produce,一個消費者consume,yield代表中斷掛起當前協程,並恢復其他協程的操作。生產者生產物品以後加入到隊列以後,中斷掛起自身並恢復消費者,消費者從隊列中消費完物品以後中斷掛起自身並恢復生產者,不斷來回切換直到達到最終條件(比如所有原料都生產成物品並全都被消費完成),程序終止。

進程、線程、協程的關係和比較

通常會提到進程是資源分配的最小單位,線程是CPU調度的最小單位, 一個進程裡可以有多個線程,這裡直接畫了個圖來說明三者關係。


對於協程(coroutine),你必須知道事情都在這裡了(內附代碼)

  • 進程是資源分配的最小單位,會擁有獨立的地址空間以及對應的內存空間,還有網絡和文件資源等,不同進程之間資源都是獨立的,可以通過進程間通信(管道、共享內存、信號量等方式)來進行交互。
  • 線程為CPU調度的基本單位,除了擁有運行中必不可少的信息(如程序計數器、一組寄存器和棧)以外,本身並不擁有系統資源,所有線程會共享進程的資源,比如會共享堆資源。
  • 協程可以認為是運行在線程上的代碼塊,協程提供的掛起操作會使協程暫停執行,而不會導致線程阻塞。一個線程內部可以創建幾千個協程都沒有任何問題。
  • 進程的切換和線程切換中都包含了對應上下文的切換,這塊都涉及到了內核來完成,即一次用戶態到內核態的切換和一次內核態到用戶態的切換。因為進程上下文切換保存的信息更多,所以進程切換代價會比線程切換代價更大。
  • 協程是一個純用戶態的併發機制,同一時刻只會有一個協程在運行,其他協程掛起等待;不同協程之間的切換不涉及內核,只用在用戶態切換即可,所以切換代價更小,更輕量級,適合IO密集型的場景。
  • coroutine的python實現

    Python最初的版本里是包含了yield/send關鍵字,通過yield/send可以方便的實現一個協程的例子,這裡還是以為生產者和消費者為例,具體實現方式如下:


    對於協程(coroutine),你必須知道事情都在這裡了(內附代碼)

    結果:

    對於協程(coroutine),你必須知道事情都在這裡了(內附代碼)

    python 3.5版本開始引入了async/await關鍵字給了我們另外一種實現的方法,還是以為生產者和消費者為例,具體實現方式如下:


    對於協程(coroutine),你必須知道事情都在這裡了(內附代碼)

    結果:

    對於協程(coroutine),你必須知道事情都在這裡了(內附代碼)


    分享到:


    相關文章: