協程
首先要明確,線程和進程都是系統幫咱們開辟的,不管是thread還是process他內部都是調用的系統的API,而對於協程來說它和系統毫無關系;
協程不同於線程的是,線程是搶占式的調度,而協程是協同式的調度,也就是說,協程需要自己做調度。
他就和程序員有關系,對於線程和進程來說,調度是由CPU來決定調度的;
對於協程來說,程序員就是上帝,你想讓誰執行到哪里他就執行到哪里;
協程存在的意義:對於多線程應用,CPU通過切片的方式來切換線程間的執行,線程切換時需要耗時(保存狀態,下次繼續)。協程,則只使用一個線程,在一個線程中規定某個代碼塊執行順序。
適用場景:其實在其他語言中,協程的其實是意義不大的多線程即可已解決I/O的問題,但是在python因為他有GIL(Global Interpreter Lock 全局解釋器鎖 )在同一時間只有一個線程在工作,所以:如果一個線程里面I/O操作特別多,協程就比較適用;
協程,又稱微線程,纖程。英文名Coroutine。一句話說明什么是線程:協程是一種用戶態的輕量級線程。
協程擁有自己的寄存器上下文和棧。協程調度切換時,將寄存器上下文和棧保存到其他地方,在切回來的時候,恢復先前保存的寄存器上下文和棧。因此:
協程能保留上一次調用時的狀態(即所有局部狀態的一個特定組合),每次過程重入時,就相當於進入上一次調用的狀態,換種說法:進入上一次離開時所處邏輯流的位置。
協程的好處:
- 無需線程上下文切換的開銷
- 無需原子操作鎖定及同步的開銷
- 方便切換控制流,簡化編程模型
- 高並發+高擴展性+低成本:一個CPU支持上萬的協程都不是問題。所以很適合用於高並發處理。
缺點:
- 無法利用多核資源:協程的本質是個單線程,它不能同時將 單個CPU 的多個核用上,協程需要和進程配合才能運行在多CPU上.當然我們日常所編寫的絕大部分應用都沒有這個必要,除非是cpu密集型應用。
- 進行阻塞(Blocking)操作(如IO時)會阻塞掉整個程序
Greenlet
from greenlet import greenlet def test1(): print(12) gr2.switch() print(34) gr2.switch() def test2(): print(56) gr1.switch() print(78) gr1 = greenlet(test1) gr2 = greenlet(test2) gr1.switch() 得到結果為: 12 56 34 78
Gevent
Gevent 是一個第三方庫,可以輕松通過gevent實現並發同步或異步編程,在gevent中用到的主要模式是Greenlet, 它是以C擴展模塊形式接入Python的輕量級協程。 Greenlet全部運行在主程序操作系統進程的內部,但它們被協作式地調度。
1 import gevent 2 3 def foo(): 4 print('Running in foo') 5 gevent.sleep(2) 6 print('Explicit context switch to foo again') 7 8 def bar(): 9 print('Explicit context to bar') 10 gevent.sleep(1) 11 print('Implicit context switch back to bar') 12 13 gevent.joinall([ 14 gevent.spawn(foo), 15 gevent.spawn(bar), 16 ]) 17 18 得到結果為: 19 Running in foo 20 Explicit context to bar 21 Implicit context switch back to bar 22 Explicit context switch to foo again
遇到IO阻塞時會自動切換任務
from gevent import monkey; monkey.patch_all() import gevent from urllib.request import urlopen def f(url): print('GET: %s' % url) resp = urlopen(url) data = resp.read() print('%d bytes received from %s.' % (len(data), url)) gevent.joinall([ gevent.spawn(f, 'https://www.python.org/'), gevent.spawn(f, 'https://www.yahoo.com/'), gevent.spawn(f, 'https://github.com/'), ])
