不懂Python中的asyncio庫(kù)-asyncio的概念是什么?其實(shí)想解決這個(gè)問(wèn)題也不難,下面讓小編帶著大家一起學(xué)習(xí)怎么去解決,希望大家閱讀完這篇文章后大所收獲。
創(chuàng)新互聯(lián)是一家集網(wǎng)站建設(shè),哈爾濱企業(yè)網(wǎng)站建設(shè),哈爾濱品牌網(wǎng)站建設(shè),網(wǎng)站定制,哈爾濱網(wǎng)站建設(shè)報(bào)價(jià),網(wǎng)絡(luò)營(yíng)銷(xiāo),網(wǎng)絡(luò)優(yōu)化,哈爾濱網(wǎng)站推廣為一體的創(chuàng)新建站企業(yè),幫助傳統(tǒng)企業(yè)提升企業(yè)形象加強(qiáng)企業(yè)競(jìng)爭(zhēng)力。可充分滿(mǎn)足這一群體相比中小企業(yè)更為豐富、高端、多元的互聯(lián)網(wǎng)需求。同時(shí)我們時(shí)刻保持專(zhuān)業(yè)、時(shí)尚、前沿,時(shí)刻以成就客戶(hù)成長(zhǎng)自我,堅(jiān)持不斷學(xué)習(xí)、思考、沉淀、凈化自己,讓我們?yōu)楦嗟钠髽I(yè)打造出實(shí)用型網(wǎng)站。
核心概念
asyncio里面主要有4個(gè)需要關(guān)注的基本概念
Eventloop
Eventloop可以說(shuō)是asyncio應(yīng)用的核心,是中央總控。Eventloop實(shí)例提供了注冊(cè)、取消和執(zhí)行任務(wù)和回調(diào)的方法。
把一些異步函數(shù)(就是任務(wù),Task,一會(huì)就會(huì)說(shuō)到)注冊(cè)到這個(gè)事件循環(huán)上,事件循環(huán)會(huì)循環(huán)執(zhí)行這些函數(shù)(但同時(shí)只能執(zhí)行一個(gè)),當(dāng)執(zhí)行到某個(gè)函數(shù)時(shí),如果它正在等待I/O返回,事件循環(huán)會(huì)暫停它的執(zhí)行去執(zhí)行其他的函數(shù);當(dāng)某個(gè)函數(shù)完成I/O后會(huì)恢復(fù),下次循環(huán)到它的時(shí)候繼續(xù)執(zhí)行。因此,這些異步函數(shù)可以協(xié)同(Cooperative)運(yùn)行:這就是事件循環(huán)的目標(biāo)。
Coroutine
協(xié)程(Coroutine)本質(zhì)上是一個(gè)函數(shù),特點(diǎn)是在代碼塊中可以將執(zhí)行權(quán)交給其他協(xié)程:
? cat coro1.py import asyncio async def a(): print('Suspending a') await asyncio.sleep(0) print('Resuming a') async def b(): print('In b') async def main(): await asyncio.gather(a(), b()) if __name__ == '__main__': asyncio.run(main())
這里面有4個(gè)重要關(guān)鍵點(diǎn):
協(xié)程要用async def聲明,Python 3.5時(shí)的裝飾器寫(xiě)法已經(jīng)過(guò)時(shí),我就不列出來(lái)了。
asyncio.gather用來(lái)并發(fā)運(yùn)行任務(wù),在這里表示協(xié)同的執(zhí)行a和b2個(gè)協(xié)程
在協(xié)程a中,有一句await asyncio.sleep(0),await表示調(diào)用協(xié)程,sleep 0并不會(huì)真的sleep(因?yàn)闀r(shí)間為0),但是卻可以把控制權(quán)交出去了。
asyncio.run是Python 3.7新加的接口,要不然你得這么寫(xiě):
loop = asyncio.get_event_loop() loop.run_until_complete(main()) loop.close()
好了,我們先運(yùn)行一下看看:
? python coro1.py Suspending a In b Resuming a
看到了吧,在并發(fā)執(zhí)行中,協(xié)程a被掛起又恢復(fù)過(guò)。
Future
接著說(shuō)Future,它代表了一個(gè)「未來(lái)」對(duì)象,異步操作結(jié)束后會(huì)把最終結(jié)果設(shè)置到這個(gè)Future對(duì)象上。Future是對(duì)協(xié)程的封裝,不過(guò)日常開(kāi)發(fā)基本是不需要直接用這個(gè)底層Future類(lèi)的。我在這里只是演示一下:
In : def c(): ...: print('Inner C') ...: return 12 ...: In : future = loop.run_in_executor(None, c) # 這里沒(méi)用await,None 表示默認(rèn)的 executor Inner C In : future # 雖然c已經(jīng)執(zhí)行了,但是狀態(tài)還是 pending。 Out: <Future pending cb=[_chain_future.<locals>._call_check_cancel() at /usr/local/lib/python3.7/asyncio/futures.py:348]> In : future.done() # 還沒(méi)有完成 Out: False In : for a in dir(future): ...: if not a.startswith('_'): ...: print(a) ...: add_done_callback cancel cancelled done exception get_loop remove_done_callback result set_exception set_result
可以對(duì)這個(gè)Future實(shí)例添加完成后的回調(diào)(add_done_callback)、取消任務(wù)(cancel)、設(shè)置最終結(jié)果(set_result)、設(shè)置異常(如果有的話(huà),set_exception)等?,F(xiàn)在我們讓Future完成:
In : await future Out: 12 In : future Out: <Future finished result=12> In : future.done() Out: True In : future.result() Out: 12
看到了吧,await之后狀態(tài)成了finished。這里順便說(shuō)一下,一個(gè)對(duì)象怎么樣就可以被await(或者說(shuō)怎么樣就成了一個(gè)awaitable對(duì)象)呢?給類(lèi)實(shí)現(xiàn)一個(gè)__await__方法,Python版本的Future的實(shí)現(xiàn)大概如下:
def __await_(self): if not self.done(): self._asyncio_future_blocking = True yield self if not self.done(): raise RuntimeError("await wasn't used with future") return self.result()
這樣就可以await future了,那為什么await future后Future的狀態(tài)就能改變呢,這是因?yàn)橛胠oop.run_in_executor創(chuàng)建的Future注冊(cè)了一個(gè)回調(diào)(通過(guò)asyncio.futures.wrap_future,加了一個(gè)_call_set_state回調(diào), 有興趣的可以通過(guò)延伸閱讀鏈接2找上下文)。
__await__里面的yield self不要奇怪,主要是為了兼容__iter__,給舊的yield from用:
In : future = loop.run_in_executor(None, c) Inner C In : future Out: <Future pending cb=[_chain_future.<locals>._call_check_cancel() at /usr/local/lib/python3.7/asyncio/futures.py:348]> In : def spam(): ...: yield from future ...: In : s = spam() In : next(s) Out: <Future pending cb=[_chain_future.<locals>._call_check_cancel() at /usr/local/lib/python3.7/asyncio/futures.py:348]> 新的替代yield from的用法await必須在異步函數(shù)(用 async def申明)中使用: In : def spam(): ...: await future ...: File "cell_name", line 5 SyntaxError: 'await' outside async function
Task
Eventloop除了支持協(xié)程,還支持注冊(cè)Future和Task2種類(lèi)型的對(duì)象,那為什么要存在Future和Task這2種類(lèi)型呢?
先回憶前面的例子,F(xiàn)uture是協(xié)程的封裝,F(xiàn)uture對(duì)象提供了很多任務(wù)方法(如完成后的回調(diào)、取消、設(shè)置任務(wù)結(jié)果等等),但是開(kāi)發(fā)者并不需要直接操作Future這種底層對(duì)象,而是用Future的子類(lèi)Task協(xié)同的調(diào)度協(xié)程以實(shí)現(xiàn)并發(fā)。
Task非常容易創(chuàng)建和使用:
# 或者用task = loop.create_task(a()) In : task = asyncio.ensure_future(a()) In : task Out: <Task pending coro=<a() running at /Users/dongwm/mp/2019-05-22/coro1.py:4>> In : task.done() Out: False In : await task Suspending a Resuming a In : task Out: <Task finished coro=<a() done, defined at /Users/dongwm/mp/2019-05-22/coro1.py:4> result=None> In : task.done() Out: True
asyncio并發(fā)的正確/錯(cuò)誤姿勢(shì)
在代碼中使用async/await是不是就能發(fā)揮asyncio的并發(fā)優(yōu)勢(shì)么,其實(shí)是不對(duì)的,我們先看個(gè)例子:
async def a(): print('Suspending a') await asyncio.sleep(3) print('Resuming a') async def b(): print('Suspending b') await asyncio.sleep(1) print('Resuming b') async def s1(): await a() await b()
有2個(gè)協(xié)程a和b,分別sleep1秒和3秒,如果協(xié)程可以并發(fā)執(zhí)行,那么執(zhí)行時(shí)間應(yīng)該是sleep最大的那個(gè)值(3秒),現(xiàn)在它們都在s1協(xié)程里面被調(diào)用。大家先猜一下s1會(huì)運(yùn)行幾秒?
我們寫(xiě)個(gè)小程序驗(yàn)證一下:
def show_perf(func): print('*' * 20) start = time.perf_counter() asyncio.run(func()) print(f'{func.__name__} Cost: {time.perf_counter() - start}')
大家注意我這個(gè)時(shí)間計(jì)數(shù)用的方法,沒(méi)有用time.time,而是用了Python 3.3新增的time.perf_counter它是現(xiàn)在推薦的用法。我們?cè)贗Python里面驗(yàn)證下:
In : from coro2 import * In : show_perf(s1) ******************** Suspending a Resuming a Suspending b Resuming b s1 Cost: 4.009796932999961
看到了吧,4秒?。?!,相當(dāng)于串行的執(zhí)行了(sleep 3 + 1)。這是錯(cuò)誤的用法,應(yīng)該怎么用呢,前面的asyncio.gather就可以:
async def c1(): await asyncio.gather(a(), b()) In : show_perf(c1) ******************** Suspending a Suspending b Resuming b Resuming a c1 Cost: 3.002452698999832
看到了吧,3秒!另外一個(gè)是asyncio.wait:
async def c2(): await asyncio.wait([a(), b()]) In : show_perf(c2) ... c2 Cost: 3.0066957049998564
同樣是3秒。先別著急,gather和wait下篇文章還會(huì)繼續(xù)對(duì)比。還有一個(gè)方案就是用asyncio.create_task:
async def c3(): task1 = asyncio.create_task(a()) task2 = asyncio.create_task(b()) await task1 await task2 async def c4(): task = asyncio.create_task(b()) await a() await task In : show_perf(c3) ... c3 Cost: 3.002332438999929 In : show_perf(c4) ... c4 Cost: 3.002270970000154
都是3秒。asyncio.create_task相當(dāng)于把協(xié)程封裝成Task。不過(guò)大家要注意一個(gè)錯(cuò)誤的用法:
async def s2(): await asyncio.create_task(a()) await asyncio.create_task(b()) In : show_perf(s2) ... s2 Cost: 4.004671427999938
直接await task不會(huì)對(duì)并發(fā)有幫助*。asyncio.create_task是Python 3.7新增的高階API,是推薦的用法,其實(shí)你還可以用
asyncio.ensure_future和loop.create_task: async def c5(): task = asyncio.ensure_future(b()) await a() await task async def c6(): loop = asyncio.get_event_loop() task = loop.create_task(b()) await a() await task In : show_perf(c5) ... c5 Cost: 3.0033873750003295 In : show_perf(c6) ... c6 Cost: 3.006120122000084
感謝你能夠認(rèn)真閱讀完這篇文章,希望小編分享Python中的asyncio庫(kù)-asyncio的概念是什么內(nèi)容對(duì)大家有幫助,同時(shí)也希望大家多多支持創(chuàng)新互聯(lián),關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,遇到問(wèn)題就找創(chuàng)新互聯(lián),詳細(xì)的解決方法等著你來(lái)學(xué)習(xí)!
標(biāo)題名稱(chēng):Python中的asyncio庫(kù)-asyncio的概念是什么
網(wǎng)頁(yè)地址:http://jinyejixie.com/article24/ghhcje.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供自適應(yīng)網(wǎng)站、網(wǎng)站設(shè)計(jì)、關(guān)鍵詞優(yōu)化、網(wǎng)站改版、電子商務(wù)、營(yíng)銷(xiāo)型網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶(hù)投稿、用戶(hù)轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話(huà):028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來(lái)源: 創(chuàng)新互聯(lián)