協程之我見

在Python中我們知道關鍵字yield可以用來實現生成器,我們可以在函數A中訪問生成器函數B,待B返回一個結果後,我們可以再次訪問B,而B會延續之前的執行狀態;這2個函數就像是2個線程一樣,在宏觀上看來在並行執行;但是與線程不同的是,線程的調度是由操作系統來完成的,對用戶包括開發者來說是完全透明的,但是在這裏2個函數的控制權切換卻是由我們來完成的。從這裏,我們就接觸到協程的雛形。

def A():
    for i in range(3):
        print("A get control")
        yield i

def B():
    for i in A():
        print("B get control: %d" % i)

if __name__ == "__main__":
    B()
 運行結果如下:
A get control
B get control: 0
A get control
B get control: 1
A get control
B get control: 2


協程,可以理解爲“用戶態的輕量級線程“,其調度需要由用戶來實現,包括協程上下文管理、切換等。其相比線程有以下優點:

1. 佔用的內存極小,Linux默認情況下線程的堆棧大小爲8192KB,協程之間共享堆,不共享棧,我們只需要爲每個協程在堆上分配相應的堆棧即可,而這個堆棧大小大概只需要數KB到數百KB,所以是非常廉價的

2. 協程調度開銷小,線程的調度需要陷入內核態,由操作系統來完成,涉及到上下文切換等,因此這個開銷是很大的;而協程的調度全部在用戶態完成

當然,協程也是有它的缺點的,因爲工作在用戶態,沒有硬中斷和系統調用(軟中斷)來打斷代碼的執行,所以一旦某個協程獲取了控制權,只有它顯示進行schedule_yield, 我們纔會有機會進行調度,所以不像線程那樣即使肆無忌憚的執行也不會出現飢餓現象,這也是協程的得名由來即協作式的。

因此我們可以使用協程來處理IO密集型的高併發場景

Python有很多基於協程的網絡庫譬如gevent、eventlet,它們都實現了自己的協程,大體思想就是:使用非阻塞的系統調用,然後觸發異常後,將控制權交給調度器,調度器處理完定時器事件後,在藉由epoll、kqueue等事件模型獲取發生的事件,將控制權交給等待事件發生的協程。最近接觸了Golang這門語言,發現它特別適合高併發編程,這也歸功於它的goroutine(協程)。








發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章