python協程與異步I/O


協程

首先要明確,線程和進程都是系統幫咱們開辟的,不管是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/'),
])

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM