日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > python >内容正文

python

pythonasyncio在哪个版本好_理解Python asyncio的简洁方式

發(fā)布時(shí)間:2025/3/21 python 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 pythonasyncio在哪个版本好_理解Python asyncio的简洁方式 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

異步IO是個(gè)好東西,在網(wǎng)絡(luò)讀寫場景中可以大大提高程序的并發(fā)能力,比如爬蟲、web服務(wù)等。這樣的好東西自然也要在Python中可以使用。不過,在漫長的Python2時(shí)代,官方并沒有推出一個(gè)自己的異步IO庫,到了Python 3.4 才推出。我們先來看看異步IO在Python中的發(fā)展歷史。

Python 異步IO的歷史

Python 2的異步IO庫

Python 2 時(shí)代官方并沒有異步IO的支持,但是有幾個(gè)第三方庫通過事件或事件循環(huán)(Event Loop)實(shí)現(xiàn)了異步IO,它們是:twisted: 是事件驅(qū)動(dòng)的網(wǎng)絡(luò)庫

gevent: greenlet + libevent(后來是libev或libuv)。通過協(xié)程(greenlet)和事件循環(huán)庫(libev,libuv)實(shí)現(xiàn)的gevent使用很廣泛。

tornado: 支持異步IO的web框架。自己實(shí)現(xiàn)了IOLOOP。

Python 3 官方的異步IO

Python 3.4 加入了asyncio 庫,使得Python有了支持異步IO的官方庫。這個(gè)庫,底層是事件循環(huán)(EventLoop),上層是協(xié)程和任務(wù)。asyncio自從3.4 版本加入到最新的 3.7版一直在改進(jìn)中。Python 3.4 剛開始的asyncio的協(xié)程還是基于生成器的,通過 yield from 語法實(shí)現(xiàn),可以通過裝飾器?@asyncio.coroutine(已過時(shí))裝飾一個(gè)函數(shù)來定義一個(gè)協(xié)程。比如:

Python 3.5 引入了兩個(gè)新的關(guān)鍵字 await 和 async 用來替換 @asyncio.coroutine 和 yield from ,從語言本身來支持異步IO。從而使得異步編程更加簡潔,并和普通的生成器區(qū)別開來。注意:對基于生成器的協(xié)程的支持已棄用,并計(jì)劃在 Python 3.10 中移除。所以,寫異步IO程序時(shí)只需使用 async 和 await 即可。Python 3.7 又進(jìn)行了優(yōu)化,把API分組為高層級(jí)API和低層級(jí)API。我們先看看下面的代碼,發(fā)現(xiàn)與上面的有什么不同?除了用 async 替換 @asyncio.coroutine 和用 await 替換 yield from 外,最大的變化就是關(guān)于eventloop的代碼不見了,只有一個(gè) async.run()。這就是 3.7 的改進(jìn),把eventloop相關(guān)的API歸入到低層級(jí)API,新引進(jìn)run()作為高層級(jí)API讓寫應(yīng)用程序的開發(fā)者調(diào)用,而不用再關(guān)心eventloop。除非你要寫異步庫(比如MySQL異步庫)才會(huì)和eventloop打交道。

理解asyncio

理解asyncio并不能,關(guān)鍵是要?jiǎng)悠鹗謥?#xff0c;接下來我們以下面代碼為例動(dòng)手實(shí)踐一番,通過實(shí)踐來理解它。

這段代碼很簡單,我們定義了兩個(gè)協(xié)程函數(shù)(在def前面加async),其中 hi()?我們把它叫做功能函數(shù),通過一個(gè) aysncio.sleep() 來模擬一個(gè)耗時(shí)的異步IO操作(比如下載網(wǎng)頁), main() 叫做入口函數(shù)。其實(shí)就是在main() 里面調(diào)用 hi() 函數(shù),通過不斷改變 main() 的行為來理解異步IO(協(xié)程函數(shù)的調(diào)用)的運(yùn)行過程。

1. 協(xié)程函數(shù)如何運(yùn)行?

首先,我們要明確一個(gè)道理,hi() 是一個(gè)協(xié)程函數(shù),直接調(diào)用它返回的是一個(gè)協(xié)程對象,并沒有真正運(yùn)行它。把main函數(shù)改成如下,我們來仔細(xì)看看協(xié)程函數(shù) hi() 的運(yùn)行。

下面是運(yùn)行結(jié)果:

代碼第19行,我們像運(yùn)行普通函數(shù)一樣運(yùn)行 hi() ,得到的a只是一個(gè)協(xié)程對象,見結(jié)果第二行:

a is: 這個(gè)協(xié)程對象 a 雖然生成了,但是還沒有運(yùn)行,它需要一個(gè)時(shí)機(jī)。也就是asyncio的事件循環(huán)正在運(yùn)行main,還沒有空去運(yùn)行它。

代碼第21行,通過 await 告訴 event_loop(事件循環(huán))?,main協(xié)程停在這里,你去運(yùn)行其它協(xié)程吧。這時(shí)候 event_loop 去執(zhí)行a協(xié)程,也就是去執(zhí)行 hi() 函數(shù)里面的代碼。等 hi() 運(yùn)行完,event_loop 再回到main協(xié)程繼續(xù)從21行開始執(zhí)行,把 hi() 的返回值賦值給b,這時(shí)候 b 的值是1。

event_loop 在整個(gè)異步IO過程中扮演一個(gè)管家的角色,在不同的協(xié)程之間切換運(yùn)行代碼,切換是通過事件來進(jìn)行的,通過 await 離開當(dāng)前協(xié)程,await 的協(xié)程完成后又回到之前的協(xié)程對應(yīng)的地方繼續(xù)執(zhí)行。

2. 協(xié)程函數(shù)如何并發(fā)?

異步IO的好處就是并發(fā),但如何實(shí)現(xiàn)呢?我們先來看一個(gè)不是并發(fā)的例子:

這次,我們把main修改成一個(gè)for循環(huán)執(zhí)行4次 hi()?,看看它運(yùn)行的結(jié)果:

整個(gè)過程從21:48:30 到 21:48:40 結(jié)束,用了10秒。而hi()的執(zhí)行時(shí)間分別是1秒,2秒,3秒,4秒總共10秒。也就是4個(gè)hi() 雖然是異步的但是順序執(zhí)行的,沒有并發(fā)。

接下來,就到了并發(fā)的實(shí)現(xiàn)了,通過 asyncio.creat_task()?即可:

通過 create_task() 我們在for循環(huán)里面生成了4個(gè)task(也是協(xié)程對象),但是這4個(gè)協(xié)程任務(wù)并沒有被執(zhí)行,它們需要等待一個(gè)時(shí)機(jī):當(dāng)前協(xié)程(main)遇到 await。

第二個(gè)for循環(huán)開始逐一 await 協(xié)程,此時(shí) event_loop 就可以空出手來去執(zhí)行那4個(gè)協(xié)程,過程大致如下:先執(zhí)行hi(1, 1) ,打印“enter hi(), 1 @21:58:35”,遇到await asyncio.sleep(1),當(dāng)前協(xié)程掛起;

接著執(zhí)行 hi(2, 2),執(zhí)行打印命令,遇到await asyncio.sleep(2)?,當(dāng)前協(xié)程掛起;

接著執(zhí)行 hi(3, 3),執(zhí)行打印命令,遇到await asyncio.sleep(3)?,當(dāng)前協(xié)程掛起;

接著執(zhí)行 hi(4, 4),執(zhí)行打印命令,遇到await asyncio.sleep(4)?,當(dāng)前協(xié)程掛起;

以上4步只是協(xié)程的切換和打印語句,執(zhí)行非常快,我們可以任務(wù)它們是同時(shí)執(zhí)行起來的。

1秒后,hi(1,1)的sleep結(jié)束它會(huì)發(fā)出事件告訴 event_loop 我await結(jié)束了,過來執(zhí)行我,event_loop 此時(shí)空閑就來執(zhí)行它,繼續(xù)執(zhí)行sleep后面的打印語句;

2秒后,hi(2,2)的sleep結(jié)束它會(huì)發(fā)出事件告訴 event_loop 我await結(jié)束了,過來執(zhí)行我,event_loop 此時(shí)空閑就來執(zhí)行它,繼續(xù)執(zhí)行sleep后面的打印語句;

3秒后,hi(3,3)的sleep結(jié)束它會(huì)發(fā)出事件告訴 event_loop 我await結(jié)束了,過來執(zhí)行我,event_loop 此時(shí)空閑就來執(zhí)行它,繼續(xù)執(zhí)行sleep后面的打印語句;

4秒后,hi(4,4)的sleep結(jié)束它會(huì)發(fā)出事件告訴 event_loop 我await結(jié)束了,過來執(zhí)行我,event_loop 此時(shí)空閑就來執(zhí)行它,繼續(xù)執(zhí)行sleep后面的打印語句;

4秒后,生成的4個(gè)協(xié)程任務(wù)就都執(zhí)行完畢。總耗時(shí)4秒,也就是我們的4個(gè)任務(wù)并發(fā)完成了。

所以,上面的代碼運(yùn)行的結(jié)果如下:

根據(jù)上面講述的執(zhí)行流程,可以看到結(jié)果對應(yīng)起來了。4個(gè)任務(wù)都是在35秒時(shí)開始執(zhí)行,以后每個(gè)1秒完成一個(gè)。main函數(shù)從35執(zhí)行到39介紹,共耗時(shí)4秒。

3. 錯(cuò)誤的運(yùn)行

上面的并發(fā)很完美,但有時(shí)候你可能會(huì)犯錯(cuò)。比如下面的main(), 你可能只是并發(fā) hi() 函數(shù),但不需要它的返回結(jié)果,于是有了下面的 main():

先猜猜會(huì)有什么樣的結(jié)果!!

你猜對了嗎?下面是運(yùn)行結(jié)果:main()的for循環(huán)只是生成了4個(gè)task協(xié)程,然后就退出了。event_loop 收到main退出的事件就空出來去執(zhí)行了那4個(gè)協(xié)程,進(jìn)去了但都碰到了sleep。然后event_loop就空閑了。這時(shí)候run() 就收到了main() 執(zhí)行完畢的事件,run() 就執(zhí)行完了,最后執(zhí)行print,整個(gè)程序就退出了。從main退出到整個(gè)程序退出就是一瞬間的事情,那4個(gè)協(xié)程還在傻傻的睡著,不,是在睡夢中死去了。

在main()中加一個(gè)sleep會(huì)出現(xiàn)什么結(jié)果:

在main()退出前,我們要先sleep 2秒,再來猜猜它的運(yùn)行結(jié)果是什么?

如果你對上面沒有sleep的過程搞清楚了,不難猜到正確的結(jié)果:

注意:main() 的退出和 hi(2, 2) 的退出順序。簡單講,main() 先sleep 2秒,hi(2, 2) 后sleep兩秒,所以main先退出。

理解了sleep(2) 的執(zhí)行過程,那么你就可以知道 sleep(4) 和 sleep(5) 的結(jié)果了。如果沒有自信的話,就自己改一下時(shí)間,運(yùn)行看看結(jié)果。

4. 如何判斷是否要把函數(shù)定義為協(xié)程函數(shù)?

定義一個(gè)協(xié)程函數(shù)很簡單,在def前面加async即可。那么如何判斷一個(gè)函數(shù)該不該定義為協(xié)程函數(shù)呢?

記住這一個(gè)原則:如果該函數(shù)是要進(jìn)行IO操作(讀寫網(wǎng)絡(luò)、讀寫文件、讀寫數(shù)據(jù)庫等),就把它定義為協(xié)程函數(shù),否則就是普通函數(shù)。

《新程序員》:云原生和全面數(shù)字化實(shí)踐50位技術(shù)專家共同創(chuàng)作,文字、視頻、音頻交互閱讀

總結(jié)

以上是生活随笔為你收集整理的pythonasyncio在哪个版本好_理解Python asyncio的简洁方式的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。