深入了解python中的協程函數
這篇文章給大家分享的內容是深入了解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)
打印: cunsumer1 product3 cunsumer2 product3 cunsumer3 product3 cunsumer4 product3 cunsumer5 product3
說明:
在生產者先執行了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 socket server=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中的協程函數的詳細內容。更多資訊請關注PHP中文網其他相關文章!

熱AI工具

Undresser.AI Undress
人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover
用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool
免費脫衣圖片

Clothoff.io
AI脫衣器

Video Face Swap
使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱門文章

熱工具

記事本++7.3.1
好用且免費的程式碼編輯器

SublimeText3漢化版
中文版,非常好用

禪工作室 13.0.1
強大的PHP整合開發環境

Dreamweaver CS6
視覺化網頁開發工具

SublimeText3 Mac版
神級程式碼編輯軟體(SublimeText3)

PHP主要是過程式編程,但也支持面向對象編程(OOP);Python支持多種範式,包括OOP、函數式和過程式編程。 PHP適合web開發,Python適用於多種應用,如數據分析和機器學習。

PHP適合網頁開發和快速原型開發,Python適用於數據科學和機器學習。 1.PHP用於動態網頁開發,語法簡單,適合快速開發。 2.Python語法簡潔,適用於多領域,庫生態系統強大。

在 Sublime Text 中運行 Python 代碼,需先安裝 Python 插件,再創建 .py 文件並編寫代碼,最後按 Ctrl B 運行代碼,輸出會在控制台中顯示。

PHP起源於1994年,由RasmusLerdorf開發,最初用於跟踪網站訪問者,逐漸演變為服務器端腳本語言,廣泛應用於網頁開發。 Python由GuidovanRossum於1980年代末開發,1991年首次發布,強調代碼可讀性和簡潔性,適用於科學計算、數據分析等領域。

Python更適合初學者,學習曲線平緩,語法簡潔;JavaScript適合前端開發,學習曲線較陡,語法靈活。 1.Python語法直觀,適用於數據科學和後端開發。 2.JavaScript靈活,廣泛用於前端和服務器端編程。

Golang在性能和可擴展性方面優於Python。 1)Golang的編譯型特性和高效並發模型使其在高並發場景下表現出色。 2)Python作為解釋型語言,執行速度較慢,但通過工具如Cython可優化性能。

在 Visual Studio Code(VSCode)中編寫代碼簡單易行,只需安裝 VSCode、創建項目、選擇語言、創建文件、編寫代碼、保存並運行即可。 VSCode 的優點包括跨平台、免費開源、強大功能、擴展豐富,以及輕量快速。

在 Notepad 中運行 Python 代碼需要安裝 Python 可執行文件和 NppExec 插件。安裝 Python 並為其添加 PATH 後,在 NppExec 插件中配置命令為“python”、參數為“{CURRENT_DIRECTORY}{FILE_NAME}”,即可在 Notepad 中通過快捷鍵“F6”運行 Python 代碼。
