深入瞭解python中的協程函數

來源:互聯網
上載者:User

本篇文章給大家分享的內容是深入瞭解python中的協程函數 ,有著一定的參考價值,有需要的朋友可以參考一下

概念:

根據維基百科給出的定義,“協程 是為非搶佔式多任務產生子程式的電腦程式組件,協程允許不同進入點在不同位置暫停或開始執行程式”。從技術的角度來說,“協程就是你可以暫停執行的函數”。如果你把它理解成“就像產生器一樣”,那麼你就想對了。

協程,又稱為微線程,看上去像是子程式,但是它和子程式又不太一樣,它在執行的過程中,可以在中斷當前的子程式後去執行別的子程式,再返回來執行之前的子程式,但是它的相關資訊還是之前的。

協程不同於線程,線程是搶佔式的調度,而協程是協同式的調度,協程需要自己做調度。
子程式調用總是一個入口,一次返回,調用順序是明確的。而協程的調用和子程式不同。協程看上去也是子程式,但執行過程中,在子程式內部可中斷,然後轉而執行別的子程式,在適當的時候再返回來接著執行。

協程的優點:

  • 協程優勢是極高的執行效率。因為子程式切換不是線程切換,而是由程式自身控制,因此,沒有線程切換的開銷,和多線程比,線程數量越多,協程的效能優勢就越明顯。用來執行協程多任務非常合適。

  • 協程沒有線程的安全問題。一個進程可以同時存在多個協程,但是只有一個協程是啟用的,而且協程的啟用和休眠又是程式員通過編程來控制,而不是作業系統控制的。

產生器實現協程原理

樣本:

def func(n):    index=0    if index<=n:        c=yield 1        print("task------{}".format(c))        index+=1f=func(3)n=next(f)print(n)try:    n=f.send(5)#程式就直接結束了    print("n是{}".format(n))except StopIteration as e:    pass
輸出列印:1task------5

解釋說明:

  • 很明顯func是一個產生器,send方法有一個參數,該參數指定的是上一次被掛起的yield語句的傳回值。

  • send需要做異常處理。

  • 總的來說,send方法和next方法唯一的區別是在執行send方法會首先把上一次掛起的yield語句的傳回值通過參數設定,從而實現與產生器方法的互動。但是需要注意,在一個產生器對象沒有執行next方法之前,由於沒有yield語句被掛起,所以執行send方法會報錯。

  • send方法的參數為None時,它與next方法完全等價。

產生器實現生產者和消費者模式:

def cunsumer():    while True:        n=yield 3        if not n:            return        print('cunsumer{}'.format(n))def product(c):    c.send(None)    n=0    while n<5:        n=n+1        r=c.send(n)        print("product{}".format(r))    c.close()c=cunsumer()product(c)
列印:cunsumer1product3cunsumer2product3cunsumer3product3cunsumer4product3cunsumer5product3

解釋說明:

在生產者裡先執行了 c.send(None),目的是先讓消費者掛起,再用send傳值,第一次傳1,消費者那裡列印1,生產者列印r是消費者yield後面的值。

greenlet 的引入

雖然CPython(標準Python)能夠通過產生器來實現協程,但使用起來還並不是很方便。

與此同時,Python的一個衍生版 Stackless Python實現了原生的協程,它更利於使用。

於是,大家開始將 Stackless 中關於協程的代碼 單獨拿出來做成了CPython的擴充包。

這就是 greenlet 的由來,因此 greenlet 是底層實現了原生協程的 C擴充庫。

代碼示意:

from greenlet import greenletimport randomimport timedef Producer():    while True:        item = random.randint(0,10)        print("生產了{}".format(item))        c.switch(item)#切換到消費者,並將item傳入消費者        time.sleep(1)def consumer():    print('我先執行')    #p.switch()    while True:        item = p.switch()#切換到生產者,並且等待生產者傳入item        print('消費了{}'.format(item))c = greenlet(consumer)#將一個普通函數變成一個協程p = greenlet(Producer)c.switch()#讓消費者先進入暫停狀態(只有恢複了才能接收資料)

greenlet 的價值:

  • 高效能的原生協程

  • 語義更加明確的顯式切換

  • 直接將函數封裝成協程,保持原有代碼風格

gevent協程

雖然,我們有了 基於 epoll 的回調式編程模式,但是卻難以使用。

即使我們可以通過配合 產生器協程 進行複雜的封裝,以簡化編程難度。
但是仍然有一個大的問題: 封裝難度大,現有代碼幾乎完全要重寫

gevent,通過封裝了 libev(基於epoll) 和 greenlet 兩個庫。
幫我們做好封裝,允許我們以類似於線程的方式使用協程。

以至於我們幾乎不用重寫原來的代碼就能充分利用 epoll 和 協程 威力。

代碼示意:

from gevent import monkey;monkey.patch_all()#會把python標準庫當中一些阻塞操作變成非阻塞import geventdef test1():    print("11")    gevent.sleep(4)#類比爬蟲請求阻塞    print("33")def test2():    print("22")    gevent.sleep(4)    print("44")gevent.joinall([gevent.spawn(test1),gevent.spawn(test2)])#joinall 阻塞當前協程,執行給定的greenlet#spawn 啟動協程,參數就是函數的名字

gevent 的價值:

遇到阻塞就切換到另一個協程繼續執行 !

  • 使用基於 epoll 的 libev 來避開阻塞。

  • 使用基於 gevent 的 高效協程 來切換執行。

  • 只在遇到阻塞的時候切換,沒有輪需的開銷,也沒有線程的開銷。

gevent實現並發伺服器

from gevent import monkey;monkey.patch_all()  #建議放在首行,會把python標準庫當中一些阻塞操作變成非阻塞import geventimport socketserver=socket.socket()server.bind(('',6666))server.listen(5)print("開始監聽")def readable(con,addr):    print("用戶端{}接入".format(addr))    while True:        data=con.recv(1024)        if data:            print(data)        else:            con.close()            breakwhile True:    con,addr=server.accept()    gevent.spawn(readable,con,addr)#將readable函數變為協程,並且把con和addr傳入其中。

gevent 協程通訊

gevent也有自己的隊列。使用方式和進線程基本一樣。

基於gevent和隊列的生產者和消費者模式

from gevent import monkey;monkey.patch_all()import geventfrom gevent.queue import Queueimport randomdef producter(queue):    while True:        item=random.randint(0,99)        print('生產了{}'.format(item))        queue.put(item)        gevent.sleep(1)def comuser(queue):    while True:        item=queue.get()        print('消費了{}'.format(item))queue=Queue()p=gevent.spawn(producter,queue)c=gevent.spawn(comuser,queue)gevent.joinall([p,c])
列印:生產了33消費了33生產了95消費了95生產了92消費了92...


相關推薦:

python中多進程+協程的使用

python中協程

Python 協程的詳細用法和例子

python 協程樣本

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.