Gevent官網文檔地址:http://www.gevent.org/contents.htmlhtml
咱們一般所說的協程Coroutine實際上是corporate routine的縮寫,直接翻譯爲協同的例程,通常咱們都簡稱爲協程。python
在linux系統中,線程就是輕量級的進程,而咱們一般也把協程稱爲輕量級的線程即微線程。linux
下面對比一下進程和協程的相同點和不一樣點:shell
相同點:
咱們均可以把他們看作是一種執行流,執行流能夠掛起,而且後面能夠在你掛起的地方恢復執行,這實際上均可以看作是continuation,關於這個咱們能夠經過在linux上運行一個hello程序來理解:編程
shell進程和hello進程:網絡
當咱們掛起一個執行流的時,咱們要保存的東西:併發
而寄存器和棧的結合就能夠理解爲上下文,上下文切換的理解:
CPU看上去像是在併發的執行多個進程,這是經過處理器在進程之間切換來實現的,操做系統實現這種交錯執行的機制稱爲上下文切換異步
操做系統保持跟蹤進程運行所需的全部狀態信息。這種狀態,就是上下文。
在任何一個時刻,操做系統都只能執行一個進程代碼,當操做系統決定把控制權從當前進程轉移到某個新進程時,就會進行上下文切換,即保存當前進程的上下文,恢復新進程的上下文,而後將控制權傳遞到新進程,新進程就會從它上次中止的地方開始。socket
不一樣點:async
既然咱們上面也說了,協程也被稱爲微線程,下面對比一下協程和線程:
咱們經過下面的圖更容易理解:
從上圖能夠看出,協程只是在單一的線程裏不一樣的協程之間切換,其實和線程很像,線程是在一個進程下,不一樣的線程之間作切換,這也多是協程稱爲微線程的緣由吧
繼續分析協程:
Gevent是一種基於協程的Python網絡庫,它用到Greenlet提供的,封裝了libevent事件循環的高層同步API。它讓開發者在不改變編程習慣的同時,用同步的方式寫異步I/O的代碼。
使用Gevent的性能確實要比用傳統的線程高,甚至高不少。但這裏不得不說它的一個坑:
既然Gevent用的是Greenlet,咱們經過下圖來理解greenlet:
每一個協程都有一個parent,最頂層的協程就是man thread或者是當前的線程,每一個協程遇到IO的時候就把控制權交給最頂層的協程,它會看那個協程的IO event已經完成,就將控制權給它。
下面是greenlet一個例子
1 from greenlet import greenlet 2 3 def test1(x,y): 4 z = gr2.switch(x+y) 5 print(z) 6 7 8 def test2(u): 9 print(u) 10 gr1.switch(42) 11 12 13 gr1 = greenlet(test1) 14 gr2 = greenlet(test2) 15 16 17 gr1.switch("hello",'world')
greenlet(run=None, parent=None): 建立一個greenlet實例.
gr.parent:每個協程都有一個父協程,當前協程結束後會回到父協程中執行,該 屬性默認是建立該協程的協程.
gr.run: 該屬性是協程實際運行的代碼. run方法結束了,那麼該協程也就結束了.
gr.switch(*args, **kwargs): 切換到gr協程.
gr.throw(): 切換到gr協程,接着拋出一個異常.
下面是gevent的一個例子:
1 import gevent 2 3 def func1(): 4 print("start func1") 5 gevent.sleep(1) 6 print("end func1") 7 8 9 def func2(): 10 print("start func2") 11 gevent.sleep(1) 12 print("end func2") 13 14 gevent.joinall( 15 [ 16 gevent.spawn(func1), 17 gevent.spawn(func2) 18 ] 19 )
gevent中也有本身的隊列,可是有一個場景我用的過程當中發現一個問題,就是若是我在協程中經過這個q來傳遞數據,若是對了是空的時候,從隊列獲取數據的那個協程就會被切換到另一個協程中,這個協程用於往隊列裏put放入數據,問題就出在,gevent不認爲這個放入數據爲IO操做,並不會切換到上一個協程中,會把這個協程的任務完成後在切換到另一個協程。我本來想要實現的效果是往對了放入數據後就會切換到get的那個協程。(或許我這裏理解有問題)下面是測試代碼:
1 import gevent 2 from gevent.queue import Queue 3 4 5 def func(): 6 for i in range(10): 7 8 print("int the func") 9 q.put("test") 10 11 def func2(): 12 for i in range(10): 13 print("int the func2") 14 res = q.get() 15 print("--->",res) 16 17 q = Queue() 18 gevent.joinall( 19 [ 20 gevent.spawn(func2), 21 gevent.spawn(func), 22 ] 23 )
這段代碼的運行效果爲:
若是我在fun函數的q.put("test")後面添加gevent.sleep(0),就會是以下效果:
本來我預測的在不修改代碼的狀況下就應該是第二個圖的結果,可是實際倒是第一個圖的結果(這個問題多是我本身沒研究明白,後面繼續研究)
就像我上面說的gevent和第三方庫配合使用會有一些問題,能夠總結爲:
python協程的庫能夠直接monkey path
C寫成的庫能夠採用豆瓣開源的greenify來打patch(這個功能本身準備後面作測試)
不過總的來講gevent目前爲止仍是有不少缺陷,而且不是官網標準庫,而在python3中有一個官網正在作而且在3.6中已經穩定的庫asyncio,這也是一個很是具備野心的庫,很是建議學習,我也準備後面深刻了解