最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python協(xié)程asyncio模塊的演變及高級用法

 更新時間:2021年05月17日 14:55:30   作者:大江狗  
網(wǎng)上很多關(guān)于Python協(xié)程asyncio模塊的教程都是基于老版Python的, 本文將以對比方式展示新老Python版本下協(xié)程的寫法有什么不同并總結(jié)了asyncio的一些高級用法, 包括如何獲取協(xié)程任務(wù)執(zhí)行結(jié)果,gather和wait方法的區(qū)別以及如何給任務(wù)添加回調(diào)函數(shù)。

Python協(xié)程及asyncio基礎(chǔ)知識

協(xié)程(coroutine)也叫微線程,是實(shí)現(xiàn)多任務(wù)的另一種方式,是比線程更小的執(zhí)行單元,一般運(yùn)行在單進(jìn)程和單線程上。因?yàn)樗詭PU的上下文,它可以通過簡單的事件循環(huán)切換任務(wù),比進(jìn)程和線程的切換效率更高,這是因?yàn)檫M(jìn)程和線程的切換由操作系統(tǒng)進(jìn)行。

Python實(shí)現(xiàn)協(xié)程的主要借助于兩個庫:asyncio和gevent。由于asyncio已經(jīng)成為python的標(biāo)準(zhǔn)庫了無需pip安裝即可使用,這意味著asyncio作為Python原生的協(xié)程實(shí)現(xiàn)方式會更加流行。本文僅會介紹asyncio模塊。如果大家對gevent也有需求,請留言,我會單獨(dú)寫篇文章介紹這個庫的使用。

asyncio 是從Python3.4引入的標(biāo)準(zhǔn)庫,直接內(nèi)置了對協(xié)程異步IO的支持。asyncio 的編程模型本質(zhì)是一個消息循環(huán),我們一般先定義一個協(xié)程函數(shù)(或任務(wù)), 從 asyncio 模塊中獲取事件循環(huán)loop,然后把需要執(zhí)行的協(xié)程任務(wù)(或任務(wù)列表)扔到 loop中執(zhí)行,就實(shí)現(xiàn)了異步IO。

定義協(xié)程函數(shù)及執(zhí)行方法的演變

在最早的Python 3.4中,協(xié)程函數(shù)是通過@asyncio.coroutine 和 yeild from 實(shí)現(xiàn)的, 如下所示。

 import asyncio
 
 @asyncio.coroutine
 def func1(i):
     print("協(xié)程函數(shù){}馬上開始執(zhí)行。".format(i))
     yield from asyncio.sleep(2)
     print("協(xié)程函數(shù){}執(zhí)行完畢!".format(i))
 
 if __name__ == '__main__':
     # 獲取事件循環(huán)
     loop = asyncio.get_event_loop()
 
     # 執(zhí)行協(xié)程任務(wù)
     loop.run_until_complete(func1(1))
 
     # 關(guān)閉事件循環(huán)
     loop.close()

這里我們定義了一個func1的協(xié)程函數(shù),我們可以使用asyncio.iscoroutinefunction來驗(yàn)證。定義好協(xié)程函數(shù)后,我們首先獲取事件循環(huán)loop,使用它的run_until_complete方法執(zhí)行協(xié)程任務(wù),然后關(guān)閉loop。

 print(asyncio.iscoroutinefunction(func1(1))) # True

Python 3.5以后引入了async/await 語法定義協(xié)程函數(shù),代碼如下所示。每個協(xié)程函數(shù)都以async聲明,以區(qū)別于普通函數(shù),對于耗時的代碼或函數(shù)我們使用await聲明,表示碰到等待時掛起,以切換到其它任務(wù)。

 import asyncio
 
 # 這是一個協(xié)程函數(shù)
 async def func1(i):
     print("協(xié)程函數(shù){}馬上開始執(zhí)行。".format(i))
     await asyncio.sleep(2)
     print("協(xié)程函數(shù){}執(zhí)行完畢!".format(i))
 
 if __name__ == '__main__':
     # 獲取事件循環(huán)
     loop = asyncio.get_event_loop()
 
     # 執(zhí)行協(xié)程任務(wù)
     loop.run_until_complete(func1(1))
 
     # 關(guān)閉事件循環(huán)
     loop.close()

Python 3.7之前執(zhí)行協(xié)程任務(wù)都是分三步進(jìn)行的,代碼有點(diǎn)冗余。Python 3.7提供了一個更簡便的asyncio.run方法,上面代碼可以簡化為:

 import asyncio
 
 async def func1(i):
     print(f"協(xié)程函數(shù){i}馬上開始執(zhí)行。")
     await asyncio.sleep(2)
     print(f"協(xié)程函數(shù){i}執(zhí)行完畢!")
 
 if __name__ == '__main__':
     asyncio.run(func1(1))

注:Python自3.6版本起可以使用f-string來對字符串進(jìn)行格式化了,相當(dāng)于format函數(shù)的簡化版。

創(chuàng)建協(xié)程任務(wù)的演變

前面的演示案例中,我們只執(zhí)行了單個協(xié)程任務(wù)(函數(shù))。實(shí)際應(yīng)用中,我們先由協(xié)程函數(shù)創(chuàng)建協(xié)程任務(wù),然后把它們加入?yún)f(xié)程任務(wù)列表,最后一起交由事件循環(huán)執(zhí)行。

根據(jù)協(xié)程函數(shù)創(chuàng)建協(xié)程任務(wù)有多種方法,其中最新的是Python 3.7版本提供的asyncio.create_task方法,如下所示:

 # 方法1:使用ensure_future方法。future代表一個對象,未執(zhí)行的任務(wù)。
 task1 = asyncio.ensure_future(func1(1))
 task2 = asyncio.ensure_future(func1(2))
 
 # 方法2:使用loop.create_task方法
 task1 = loop.create_task(func1(1))
 task2 = loop.create_task(func1(2))
 
 # 方法3:使用Python 3.7提供的asyncio.create_task方法
 task1 = asyncio.create_task(func1(1))
 task2 = asyncio.create_task(func1(2))

創(chuàng)建多個協(xié)程任務(wù)列表后,我們還要使用asyncio.wait方法收集協(xié)程任務(wù),并交由事件循環(huán)處理執(zhí)行。

 import asyncio
 
 async def func1(i):
     print(f"協(xié)程函數(shù){i}馬上開始執(zhí)行。")
     await asyncio.sleep(2)
     print(f"協(xié)程函數(shù){i}執(zhí)行完畢!")
 
 
 async def main():
     tasks = []
     # 創(chuàng)建包含4個協(xié)程任務(wù)的列表
     for i in range(1, 5):
         tasks.append(asyncio.create_task(func1(i)))
         
     await asyncio.wait(tasks)
 
 if __name__ == '__main__':
     asyncio.run(main())

執(zhí)行效果如下所示,你會發(fā)現(xiàn)4個協(xié)程任務(wù)并不是按順序執(zhí)行的。

對于收集多個協(xié)程任務(wù),Python還提供了新的asyncio.gather方法,它的作用asyncio.wait方法類似,但更強(qiáng)大。如果列表中傳入的不是create_task方法創(chuàng)建的協(xié)程任務(wù),它會自動將函數(shù)封裝成協(xié)程任務(wù),如下所示:

 import asyncio
 
 async def func1(i):
     print(f"協(xié)程函數(shù){i}馬上開始執(zhí)行。")
     await asyncio.sleep(2)
     print(f"協(xié)程函數(shù){i}執(zhí)行完畢!")
 
 async def main():
     tasks = []
     for i in range(1, 5):
         # 這里未由協(xié)程函數(shù)創(chuàng)建協(xié)程任務(wù)
         tasks.append(func1(i))
         
     # 注意這里*號。gather自動將函數(shù)列表封裝成了協(xié)程任務(wù)。
     await asyncio.gather(*tasks)
 
 if __name__ == '__main__':
     asyncio.run(main())

獲取協(xié)程任務(wù)執(zhí)行結(jié)果

是的,gather方法有將函數(shù)封裝成協(xié)程任務(wù)的能力,但這還并不是兩者最主要的區(qū)別作用。兩者更大的區(qū)別在協(xié)程任務(wù)執(zhí)行完畢后對于返回結(jié)果的處理上。通常獲取任務(wù)執(zhí)行結(jié)果通常對于一個程序至關(guān)重要,因此我們有必要花更多時間詳細(xì)了解這兩個方法的使用。

asyncio.wait 會返回兩個值:done 和 pending,done 為已完成的協(xié)程任務(wù)列表,pending 為超時未完成的協(xié)程任務(wù)類別,需通過task.result()方法可以獲取每個協(xié)程任務(wù)返回的結(jié)果;而asyncio.gather 返回的是所有已完成協(xié)程任務(wù)的 result,不需要再進(jìn)行調(diào)用或其他操作,就可以得到全部結(jié)果。

我們來看兩個示例?,F(xiàn)在修改我們的協(xié)程函數(shù),通過return給它增加一個返回值。

通過asyncio.wait獲取協(xié)程任務(wù)執(zhí)行結(jié)果

 import asyncio
 
 async def func1(i):
     print(f"協(xié)程函數(shù){i}馬上開始執(zhí)行。")
     await asyncio.sleep(2)
     return i
 
 async def main():
     tasks = []
     for i in range(1, 5):
         tasks.append(asyncio.create_task(func1(i)))
         
     # 獲取任務(wù)執(zhí)行結(jié)果。
     done, pending = await asyncio.wait(tasks)
     for task in done:
         print(f"執(zhí)行結(jié)果: {task.result()}")
 
 if __name__ == '__main__':
     asyncio.run(main())

執(zhí)行結(jié)果如下所示。你可以看到協(xié)程任務(wù)執(zhí)行結(jié)果并不是按任務(wù)添加的順序返回的。

通過asyncio.gather獲取協(xié)程任務(wù)執(zhí)行結(jié)果

繼續(xù)修改我們的代碼:

 #-*- coding:utf-8 -*-
 import asyncio
 
 async def func1(i):
     print(f"協(xié)程函數(shù){i}馬上開始執(zhí)行。")
     await asyncio.sleep(2)
     return i
 
 async def main():
     tasks = []
     for i in range(1, 5):
         tasks.append(func1(i))
 
     results = await asyncio.gather(*tasks)
     for result in results:
         print(f"執(zhí)行結(jié)果: {result}")
 
 if __name__ == '__main__':
     asyncio.run(main())

執(zhí)行結(jié)果如下所示。協(xié)程任務(wù)執(zhí)行結(jié)果與任務(wù)添加順序完全一致。

現(xiàn)在你知道gather和wait方法的真正區(qū)別了嗎?

  • gather具有把普通協(xié)程函數(shù)包裝成協(xié)程任務(wù)的能力,wait沒有。wait只能接收包裝后的協(xié)程任務(wù)列表做參數(shù)。
  • 兩者返回值不一樣,wait返回的是已完成和未完成任務(wù)的列表,而gather直接返回協(xié)程任務(wù)執(zhí)行結(jié)果。
  • gather返回的任務(wù)執(zhí)行結(jié)果是有序的,wait方法獲取的結(jié)果是無序的。

asyncio高級使用方法

給任務(wù)添加回調(diào)函數(shù)

我們還可以給每個協(xié)程任務(wù)通過add_done_callback的方法給單個協(xié)程任務(wù)添加回調(diào)函數(shù),如下所示:

 #-*- coding:utf-8 -*-
 import asyncio
 
 async def func1(i):
     print(f"協(xié)程函數(shù){i}馬上開始執(zhí)行。")
     await asyncio.sleep(2)
     return i
 
 # 回調(diào)函數(shù)
 def callback(future):
     print(f"執(zhí)行結(jié)果:{future.result()}")
 
 async def main():
     tasks = []
     for i in range(1, 5):
         task = asyncio.create_task(func1(i))
         
         # 注意這里,增加回調(diào)函數(shù)
         task.add_done_callback(callback)
         tasks.append(task)
 
     await asyncio.wait(tasks)
 
 if __name__ == '__main__':
     asyncio.run(main())

設(shè)置任務(wù)超時

很多協(xié)程任務(wù)都是很耗時的,當(dāng)你使用wait方法收集協(xié)程任務(wù)時,可通過timeout選項(xiàng)設(shè)置任務(wù)切換前單個任務(wù)最大等待時間長度,如下所示:

  # 獲取任務(wù)執(zhí)行結(jié)果,如下所示:
  done,pending = await asyncio.wait(tasks, timeout=10)

自省

  • asyncio.current_task: 返回當(dāng)前運(yùn)行的Task實(shí)例,如果沒有正在運(yùn)行的任務(wù)則返回 None。如果 loop 為 None 則會使用 get_running_loop()獲取當(dāng)前事件循環(huán)。
  • asyncio.all_tasks: 返回事件循環(huán)所運(yùn)行的未完成的Task對象的集合。

以上就是Python協(xié)程asyncio模塊的演變及高級用法的詳細(xì)內(nèi)容,更多關(guān)于Python協(xié)程asyncio模塊的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 如何用Python徒手寫線性回歸

    如何用Python徒手寫線性回歸

    這篇文章主要介紹了如何用Python徒手寫線性回歸,幫助大家更好的理解和使用python,感興趣的朋友可以了解下
    2021-01-01
  • OpenCV哈里斯角檢測|Harris?Corner理論實(shí)踐

    OpenCV哈里斯角檢測|Harris?Corner理論實(shí)踐

    這篇文章主要為大家介紹了OpenCV哈里斯角檢測|Harris?Corner理論實(shí)踐,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-04-04
  • numpy linalg模塊的具體使用方法

    numpy linalg模塊的具體使用方法

    這篇文章主要介紹了numpy linalg模塊的具體使用方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-05-05
  • python tkinter Entry控件的焦點(diǎn)移動操作

    python tkinter Entry控件的焦點(diǎn)移動操作

    這篇文章主要介紹了python tkinter Entry控件的焦點(diǎn)移動操作,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-05-05
  • 詳解基于Jupyter notebooks采用sklearn庫實(shí)現(xiàn)多元回歸方程編程

    詳解基于Jupyter notebooks采用sklearn庫實(shí)現(xiàn)多元回歸方程編程

    這篇文章主要介紹了詳解基于Jupyter notebooks采用sklearn庫實(shí)現(xiàn)多元回歸方程編程,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-03-03
  • django項(xiàng)目中使用手機(jī)號登錄的實(shí)例代碼

    django項(xiàng)目中使用手機(jī)號登錄的實(shí)例代碼

    這篇文章主要介紹了django項(xiàng)目中使用手機(jī)號登錄的實(shí)例代碼,非常不錯,具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下
    2019-08-08
  • python3.6下Numpy庫下載與安裝圖文教程

    python3.6下Numpy庫下載與安裝圖文教程

    這篇文章主要為大家詳細(xì)介紹了python Numpy庫下載與安裝圖文教程,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-04-04
  • Python 時間操作datetime詳情(下)

    Python 時間操作datetime詳情(下)

    我們在上一文datetime模塊對datetime模塊中包含了六個類對象如date、time、datetime等對時間和日期進(jìn)行操作。這一篇文章我們繼續(xù)上一篇文章的內(nèi)容學(xué)習(xí)datetime模塊中六大類的相關(guān)方法和屬性
    2021-11-11
  • Python中使用urllib2防止302跳轉(zhuǎn)的代碼例子

    Python中使用urllib2防止302跳轉(zhuǎn)的代碼例子

    這篇文章主要介紹了Python中使用urllib2防止302跳轉(zhuǎn)的代碼例子,即避免302跳轉(zhuǎn)的實(shí)現(xiàn),需要的朋友可以參考下
    2014-07-07
  • 一行Python代碼實(shí)現(xiàn)為圖片上版權(quán)

    一行Python代碼實(shí)現(xiàn)為圖片上版權(quán)

    不知道大家會不會遇到這樣的情況,自己辛辛苦苦整理的攻略,分享給自己的一些朋友,結(jié)果分享有人堂而皇之地拿著這份攻略圖片去引流,并聲稱是自己整理的,真是豈有此理!本文就來用Python實(shí)現(xiàn)為圖片上版權(quán),需要的可以參考一下
    2023-01-01

最新評論

常州市| 锦屏县| 丹江口市| 锡林浩特市| 连南| 浮山县| 平和县| 南漳县| 巴里| 石林| 翁源县| 墨竹工卡县| 三门县| 灵石县| 张家界市| 绥滨县| 温州市| 若尔盖县| 仙游县| 清水河县| 西城区| 武义县| 大厂| 长春市| 东港市| 高碑店市| 班戈县| 乐陵市| 万年县| 大庆市| 齐齐哈尔市| 界首市| 馆陶县| 宾阳县| 缙云县| 榕江县| 安岳县| 凌海市| 开江县| 聂荣县| 岢岚县|