python協(xié)程異步IO中asyncio的使用
async await介紹
用asyncio提供的@asyncio.coroutine可以把一個(gè)生成器標(biāo)記為協(xié)程類(lèi)型,然后在協(xié)程內(nèi)部用yield from 等待IO操作,讓出cpu執(zhí)行權(quán)。
然而異步的關(guān)鍵字yield 和 yield from畢竟是復(fù)用生成器關(guān)鍵字,兩者在概念上糾纏不清,所以從Python 3.5開(kāi)始引入了新的語(yǔ)法async和await替換yield 和 yield from,讓協(xié)程的代碼更易懂。
簡(jiǎn)單來(lái)說(shuō),可以這樣理解:
- async 替換
@asyncio.coroutine:標(biāo)識(shí)一個(gè)函數(shù)為異步函數(shù) - await 替換 yield from:標(biāo)識(shí)等待IO操作,讓出CPU執(zhí)行權(quán)
async實(shí)現(xiàn)協(xié)程示例
由于協(xié)程在各個(gè)python版本中有細(xì)微差異,本篇以python3.10為例
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)建任務(wù)
task1 = loop.create_task(coro1())
task2 = loop.create_task(coro2())
# 運(yùn)行協(xié)程
loop.run_until_complete(asyncio.gather(task1, task2))
# 關(guān)閉事件循環(huán)
loop.close()輸出結(jié)果:
start coro1
start coro2
end coro2
end coro1
代碼邏輯:
- 創(chuàng)建一個(gè)事件循環(huán)
- 將兩個(gè)異步函數(shù)coro1,coro2封裝成兩個(gè)任務(wù)task1,task2
- 用asyncio.gather將兩個(gè)任務(wù)組合到一起,并發(fā)執(zhí)行task1,task2
- 先執(zhí)行task1,遇到IO切換到task2
- 執(zhí)行task2,遇到IO切換,但此時(shí)沒(méi)有等待執(zhí)行的任務(wù),cpu為空
- task2執(zhí)行完成,task1執(zhí)行完成
從示例代碼可以看出,協(xié)程的幾個(gè)關(guān)鍵要素:
- 事件循環(huán)
- 協(xié)程函數(shù)定義
- 可等待對(duì)象
- 并發(fā)執(zhí)行
協(xié)程基本原理
組成協(xié)程最重要的因素就是事件循環(huán)和任務(wù)。
- 任務(wù)就是一個(gè)對(duì)象,包括執(zhí)行的代碼,執(zhí)行完成、失敗等狀態(tài)以及返回結(jié)果,任務(wù)中通常會(huì)有IO切換。
- 事件循環(huán),可以把它當(dāng)做是一個(gè)while循環(huán)。while循環(huán)在周期性的運(yùn)行并執(zhí)行一些任務(wù),所有任務(wù)執(zhí)行完成會(huì)關(guān)閉循環(huán)。
偽代碼示例如下:
任務(wù)列表 = [ 任務(wù)1, 任務(wù)2, 任務(wù)3,... ]
while True:
可執(zhí)行的任務(wù)列表,已完成的任務(wù)列表 = 去任務(wù)列表中檢查所有的任務(wù),將'可執(zhí)行'和'已完成'的任務(wù)返回
for 就緒任務(wù) in 已準(zhǔn)備就緒的任務(wù)列表:
執(zhí)行已就緒的任務(wù)
for 已完成的任務(wù) in 已完成的任務(wù)列表:
在任務(wù)列表中移除 已完成的任務(wù)如果 任務(wù)列表 中的任務(wù)都已完成,則終止循環(huán)
獲取和創(chuàng)建事件循環(huán):loop = asyncio.get_event_loop()
驅(qū)動(dòng)事件循環(huán)運(yùn)行:loop.run_until_complete(asyncio.gather(task1, task2))
事件循環(huán)過(guò)程:
事件循環(huán)中執(zhí)行任務(wù),當(dāng)執(zhí)行到某一個(gè)任務(wù)時(shí)遇到IO時(shí),協(xié)程會(huì)讓出CPU給第二個(gè)任務(wù)執(zhí)行,第二個(gè)任務(wù)中遇到IO再次讓出CPU,直到所有任務(wù)完成。這就是協(xié)程并發(fā)性能好的一個(gè)關(guān)鍵能力:遇到IO切換任務(wù)執(zhí)行,避免了程序等待IO完成再執(zhí)行的耗時(shí)。
示例代碼的高級(jí)api實(shí)現(xiàn)
示例代碼中使用了asyncio.get_event_loop()和 loop.run_until_complete()等代碼,這些其實(shí)asyncio包的低級(jí)API,是為了展示底層原理而使用的。通常更推薦高級(jí)APIasyncio.run()實(shí)現(xiàn)協(xié)程并發(fā)。
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")
async def main():
task1 = asyncio.create_task(coro1())
task2 = asyncio.create_task(coro2())
await asyncio.gather(task1, task2)
asyncio.run(main())run() 從功能上等價(jià)于以下低階API
loop = asyncio.get_event_loop() task = loop.create_task(coro()) loop.run_until_complete(task)
為什么協(xié)程在IO密集時(shí)性能較好
很多人可能會(huì)疑問(wèn),多線程遇到IO也會(huì)切換,為什么協(xié)程比線程性能好呢?
簡(jiǎn)單來(lái)是三點(diǎn):
- 協(xié)程更輕量級(jí),切換需要恢復(fù)的上下文很少,所以比線程更快速
- 線程切換CPU是搶占的,協(xié)程是主動(dòng)讓出的,協(xié)程對(duì)CPU的使用更充分
- 協(xié)程更輕量級(jí),啟動(dòng)線程需要的內(nèi)存資源比協(xié)程更多
以上就是python協(xié)程異步IO中asyncio的使用的詳細(xì)內(nèi)容,更多關(guān)于python asyncio的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- Python使用asyncio實(shí)現(xiàn)異步操作的示例
- Python中asyncio的多種用法舉例(異步同步)
- Python使用asyncio處理異步編程的代碼示例
- Python使用asyncio包實(shí)現(xiàn)異步編程方式
- Python異步庫(kù)asyncio、aiohttp詳解
- Python使用asyncio標(biāo)準(zhǔn)庫(kù)對(duì)異步IO的支持
- Python協(xié)程異步爬取數(shù)據(jù)(asyncio+aiohttp)實(shí)例
- Python使用asyncio異步時(shí)的常見(jiàn)問(wèn)題總結(jié)
- Python asyncio異步編程常見(jiàn)問(wèn)題小結(jié)
- Python asyncio異步編程簡(jiǎn)單實(shí)現(xiàn)示例
- Python中asyncio庫(kù)實(shí)現(xiàn)異步編程的示例
相關(guān)文章
phpsir 開(kāi)發(fā) 一個(gè)檢測(cè)百度關(guān)鍵字網(wǎng)站排名的python 程序
一個(gè)檢測(cè)百度關(guān)鍵字網(wǎng)站排名的python 程序 phpsir 開(kāi)發(fā)2009-09-09
python實(shí)現(xiàn)0到1之間的隨機(jī)數(shù)方式
這篇文章主要介紹了python實(shí)現(xiàn)0到1之間的隨機(jī)數(shù)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07
Python進(jìn)行數(shù)據(jù)提取的方法總結(jié)
數(shù)據(jù)提取是分析師日常工作中經(jīng)常遇到的需求。如某個(gè)用戶(hù)的貸款金額,某個(gè)月或季度的利息總收入,某個(gè)特定時(shí)間段的貸款金額和筆數(shù),大于5000元的貸款數(shù)量等等。本篇文章介紹如何通過(guò)python按特定的維度或條件對(duì)數(shù)據(jù)進(jìn)行提取,完成數(shù)據(jù)提取需求。2016-08-08
PyMySQL數(shù)據(jù)庫(kù)連接與優(yōu)化方式
本文介紹PyMySQL連接MySQL數(shù)據(jù)庫(kù)的方法,涵蓋CRUD操作、事務(wù)處理及連接池優(yōu)化,通過(guò)合理配置連接池和錯(cuò)誤重試機(jī)制,提升性能并確保數(shù)據(jù)一致性2025-10-10
Django在admin后臺(tái)集成TinyMCE富文本編輯器的例子
今天小編就為大家分享一篇Django在admin后臺(tái)集成TinyMCE富文本編輯器的例子,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2019-08-08

