Python異步編程之新舊協(xié)程的實現(xiàn)對比
1、協(xié)程的發(fā)展流程
再來回顧一下協(xié)程的發(fā)展流程:
- python2.5 為生成器引用.send()、.throw()、.close()方法
- python3.3 為引入yield from,可以接收返回值,可以使用yield from定義協(xié)程
- Python3.4 加入了asyncio模塊
- Python3.5 增加async、await關鍵字,在語法層面的提供支持
- python3.7 使用 async def + await 的方式定義協(xié)程
- python3.10 移除 以 yield from 的方式定義協(xié)程
舊協(xié)程是指以yield、yield from等生成器語法為基礎的協(xié)程實現(xiàn)
新協(xié)程是指以asyncio、async、await等關鍵字為基礎的協(xié)程實現(xiàn)
兩種協(xié)程的實現(xiàn)方式在協(xié)程發(fā)展史上有一段交集,并且舊協(xié)程基于生成器的協(xié)程語法讓生成器和協(xié)程兩個概念混淆,所以對學習者會造成一定的困擾。本篇主要說明兩種協(xié)程的實現(xiàn)方式的差異。
2、舊協(xié)程回顧
舊協(xié)程以yield關鍵字為核心,通過yield關鍵提供的代碼執(zhí)行暫停、恢復的能力,實現(xiàn)函數(shù)交替的執(zhí)行,cpu的轉讓等能力。
import time
def consume():
r = ''
while True:
n = yield r
print(f'[consumer] 開始消費 {n}...')
time.sleep(1)
r = f'{n} 消費完成'
def produce(c):
next(c)
n = 0
while n < 5:
n = n + 1
print(f'[producer] 生產(chǎn)了 {n}...')
r = c.send(n)
print(f'[producer] consumer return: {r}')
c.close()
if __name__=='__main__':
c = consume()
produce(c)
執(zhí)行結果:
[producer] 生產(chǎn)了 1...
[consumer] 開始消費 1...
[producer] consumer return: 1 消費完成
[producer] 生產(chǎn)了 2...
[consumer] 開始消費 2...
[producer] consumer return: 2 消費完成
[producer] 生產(chǎn)了 3...
[consumer] 開始消費 3...
[producer] consumer return: 3 消費完成
[producer] 生產(chǎn)了 4...
[consumer] 開始消費 4...
[producer] consumer return: 4 消費完成
[producer] 生產(chǎn)了 5...
[consumer] 開始消費 5...
[producer] consumer return: 5 消費完成
結果分析:
當消費者consume執(zhí)行到n = yield r時,流程暫停,將cpu交還給調(diào)用方produce。
在asyncio初識篇中提到過,協(xié)程最重要的兩個因素是事件循環(huán)+ 任務。用yield實現(xiàn)的協(xié)程中,consume和 produce中的 while循環(huán)共同作用下實現(xiàn)了一個事件循環(huán)的功能,yield 和 send實現(xiàn)了任務的暫停和繼續(xù)執(zhí)行。
總結來說協(xié)程需要的兩個能力事件循環(huán)和任務暫停和繼續(xù),在舊協(xié)程中的實現(xiàn)分別是:
- 事件循環(huán)通過手動編寫while循環(huán)代碼實現(xiàn)
- 代碼暫停繼續(xù)執(zhí)行通過yield生成器的能力實現(xiàn)
3、新協(xié)程回顧
新協(xié)程是asyncio、async、await等關鍵字實現(xiàn)的。新協(xié)程是基于事件循環(huán)機制實現(xiàn)的,核心能力包括事件循環(huán),任務,回調(diào)機制等。三者提供的能力分別是
- asyncio 提供了事件循環(huán)
- async 提供了協(xié)程標識
- await 提供了流程掛起能力
import asyncio
async def coro1():
print("start coro1")
await asyncio.sleep(2)
print("end coro1")
async def coro2():
print("start coro2")
await asyncio.sleep(1)
print("end coro2")
# 創(chuàng)建事件循環(huán)
loop = asyncio.get_event_loop()
# 創(chuàng)建任務
task1 = loop.create_task(coro1())
task2 = loop.create_task(coro2())
# 運行協(xié)程
loop.run_until_complete(asyncio.gather(task1, task2))
# 關閉事件循環(huán)
loop.close()
結果
start coro1
start coro2
end coro2
end coro1
結果分析:
當coro1執(zhí)行到 await asyncio.sleep(2)時,流程掛起,將cpu交還給事件循環(huán),等待事件循環(huán)的下一次調(diào)度,而事件循環(huán)調(diào)度到coro2繼續(xù)執(zhí)行。
協(xié)程的兩個重要能力事件循環(huán)和 任務暫停和繼續(xù) ,分別的實現(xiàn)者:
- 事件循環(huán)通過asyncio提供的loop實現(xiàn)
- 程序掛起通過 await 關鍵字實現(xiàn)
4、新舊協(xié)程實現(xiàn)的對比
asyncio 和 yield 是用于實現(xiàn)異步編程的兩種不同的機制。
yield 是一種用于生成器(Generator)函數(shù)的關鍵字,用于創(chuàng)建可暫停和恢復執(zhí)行的函數(shù)。當一個函數(shù)中包含 yield 語句時,它會返回一個生成器對象,可以通過調(diào)用生成器的 next() 方法或使用 for 循環(huán)來逐步迭代生成器函數(shù)中的值。
通過使用 yield,我們可以將一個函數(shù)分割成多個代碼塊,并在每個代碼塊之間進行切換執(zhí)行。這使得我們可以在函數(shù)執(zhí)行過程中臨時掛起函數(shù)的執(zhí)行,然后再次恢復執(zhí)行。
asyncio 是 Python 提供的標準庫,用于編寫異步代碼。它基于事件循環(huán)(Event Loop)模式,允許我們在單線程中處理多個并發(fā)任務,并通過協(xié)程(Coroutine)來管理異步操作。
asyncio 使用了 async 和 await 這兩個關鍵字來定義協(xié)程函數(shù)。在協(xié)程函數(shù)中可以使用 await 關鍵字來暫停當前協(xié)程的執(zhí)行,等待某個異步操作的完成,然后恢復執(zhí)行。
總結來說:
舊協(xié)程:通過yield關鍵字的暫停和恢復執(zhí)行的能力實現(xiàn)協(xié)程
新協(xié)程:通過事件循環(huán)機制,await關鍵字掛起流程能力實現(xiàn)協(xié)程
5、await 和 yield 的關系
await 關鍵字和 yield 關鍵字都可以用于控制流的暫停和恢復,都屬于python的關鍵字,但是它們在協(xié)程的實現(xiàn)上有所不同。
相同點:
- 控制流暫停和恢復:無論是
await還是yield,它們都可以使代碼在某個點暫停執(zhí)行,并在稍后的時間點繼續(xù)執(zhí)行。 - 協(xié)程支持:
await和yield都與協(xié)程(Coroutine)密切相關。它們都能夠用于定義和管理協(xié)程,使得異步代碼的編寫更加簡單和易讀。
區(qū)別:
1.語法差異:await 是 Python 3.5 引入的關鍵字,用于異步函數(shù)中暫停執(zhí)行等待異步操作完成。而 yield 是早期協(xié)程的關鍵字,主要用于生成器(Generator)函數(shù),用于創(chuàng)建迭代器和實現(xiàn)惰性計算,早期通過生成器的能力來實現(xiàn)協(xié)程。
2.語義:
await 表示當前協(xié)程需要等待一個異步操作的完成,并掛起執(zhí)行,讓其他任務有機會執(zhí)行。
yield 是將執(zhí)行的控制權交給調(diào)用方,同時保存函數(shù)的狀態(tài),以便在下次迭代時從上一次暫停的位置恢復執(zhí)行。
await將程序掛起,讓事件循環(huán)調(diào)度新的任務。yield將程序掛起,等待調(diào)用方的下一步指令。
3.上下文:await 必須在異步上下文中使用,例如在異步函數(shù)中或者在 async with 塊中。而 yield 可以在普通函數(shù)中使用,即使沒有使用協(xié)程的上下文。
4.返回值:yield 返回生成器對象,通過調(diào)用 next() 方法或使用 for 循環(huán)逐步迭代生成器中的值。而 await 返回一個可等待對象(Awaitable),它可以是 Future、Task、Coroutine 等。
總結:
await 不是通過 yield 來實現(xiàn)的程序暫停和執(zhí)行,兩者有相似的能力,但完全沒有調(diào)用關系,都是屬于python關鍵字。
await適用于異步編程場景,用于等待異步操作的完成,同時支持更靈活的協(xié)程管理。yield則主要用于生成器函數(shù),用于實現(xiàn)迭代器和惰性計算。
它們在應用場景和語法上存在一些差異,但都為我們提供了控制流的暫停和恢復的能力。
以上就是Python異步編程之新舊協(xié)程的實現(xiàn)對比的詳細內(nèi)容,更多關于Python協(xié)程的資料請關注腳本之家其它相關文章!
相關文章
使用Pandas和Matplotlib進行數(shù)據(jù)清洗與可視化的實現(xiàn)步驟
在數(shù)據(jù)科學領域,數(shù)據(jù)清洗和可視化是構建數(shù)據(jù)驅動解決方案的重要步驟,本文將詳細介紹如何使用Pandas進行數(shù)據(jù)清洗,并結合Matplotlib進行可視化,文章通過實際代碼示例講解的非常詳細,需要的朋友可以參考下2024-08-08
win10下tensorflow和matplotlib安裝教程
這篇文章主要為大家詳細介紹了win10下tensorflow和matplotlib安裝教程,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-09-09
python中文分詞+詞頻統(tǒng)計的實現(xiàn)步驟
詞頻統(tǒng)計就是輸入一段句子或者一篇文章,然后統(tǒng)計句子中每個單詞出現(xiàn)的次數(shù),下面這篇文章主要給大家介紹了關于python中文分詞+詞頻統(tǒng)計的相關資料,需要的朋友可以參考下2022-06-06
Python爬蟲爬取一個網(wǎng)頁上的圖片地址實例代碼
這篇文章主要介紹了Python爬蟲爬取一個網(wǎng)頁上的圖片地址實例代碼,具有一定借鑒價值,需要的朋友可以參考下2018-01-01

