Python使用Asyncio進(jìn)行web編程方法詳解
前言
許多 Web 應(yīng)用依賴大量的 I/O (輸入/輸出) 操作,比如從網(wǎng)站上下載圖片、視頻等內(nèi)容;進(jìn)行網(wǎng)絡(luò)聊天或者針對(duì)后臺(tái)數(shù)據(jù)庫(kù)進(jìn)行多次查詢。數(shù)據(jù)庫(kù)查詢可能會(huì)耗費(fèi)大量時(shí)間,尤其是在該數(shù)據(jù)庫(kù)處于高負(fù)載或查詢很復(fù)雜的情況下。 Web 服務(wù)器可能需要同時(shí)處理數(shù)百或數(shù)千個(gè)請(qǐng)求。
I/O 是指計(jì)算機(jī)的輸入和輸出設(shè)備,例如鍵盤(pán)、硬盤(pán)驅(qū)動(dòng)器,以及最常見(jiàn)的網(wǎng)卡。這些操作等待用戶輸入或從基于 Web 的 API 檢索內(nèi)容。
Asynchronous IO (async IO) 是一種異步編程設(shè)計(jì),并在 Python 3.4 的 asyncio 模塊中得到了支持,作為在多線程和多進(jìn)程之外處理這些高并發(fā)工作負(fù)載的另一種方法,可以顯著提高使用 I/O 操作的應(yīng)用程序的性能和資源利用率。
什么是同步編程
同步編程,通常來(lái)說(shuō),大多數(shù)編程語(yǔ)言都是子例程調(diào)用模型:按照順序運(yùn)行代碼。在此模型中,下一行代碼在前一行代碼完成后立即運(yùn)行,并且一次只完成一個(gè)模塊。
該模型適用于大部分應(yīng)用程序。但是,也存在明顯的缺點(diǎn),如果一行代碼特別慢怎么辦?
在這種情況下,速度慢的代碼將導(dǎo)致所有其他代碼都將被卡住,直到該行完成。最差的情況下可能導(dǎo)致整個(gè)應(yīng)用程序卡死??赡艽蠖鄶?shù)人在某些軟件操作中,一個(gè)小小的操作導(dǎo)致整個(gè)系統(tǒng)執(zhí)行不下去,最后只能重啟。
什么是異步編程
為了解決同步模型的問(wèn)題,引入了異步編程的概念,意味著允許同一時(shí)刻執(zhí)行多個(gè)任務(wù)。
異步編程模型意味著需要長(zhǎng)時(shí)間運(yùn)行的任務(wù)可以在后臺(tái)運(yùn)行,與主應(yīng)用程序分開(kāi)。系統(tǒng)可以自由地執(zhí)行不依賴于該任務(wù)的其他工作,而不是阻止所有其他應(yīng)用程序代碼等待該長(zhǎng)時(shí)間運(yùn)行的任務(wù)完成。然后,一旦長(zhǎng)時(shí)間運(yùn)行的任務(wù)完成,我們會(huì)收到通知它已完成。
asyncio 庫(kù)允許我們使用異步編程模型運(yùn)行代碼。 這讓我們可以一次處理多個(gè) I/O 操作,同時(shí)仍然允許我們的應(yīng)用程序保持響應(yīng)。
在 Python 3.4 中,asyncio 庫(kù)中包含了裝飾器和生成器 yield from 來(lái)定義協(xié)程(coroutine)。協(xié)程是一種方法,當(dāng)我們有一個(gè)可能長(zhǎng)時(shí)間運(yùn)行的任務(wù)時(shí)可以暫停,然后在該任務(wù)完成時(shí)恢復(fù)。
協(xié)程執(zhí)行完成后返回到調(diào)用者有一種新方法:通過(guò) yield 控制。當(dāng)協(xié)程的 yield 執(zhí)行完成后立即回到了調(diào)用點(diǎn),但是對(duì)協(xié)程的再次調(diào)用不會(huì)在起始處再次開(kāi)始,相反,他們繼續(xù)從最近停止處繼續(xù)進(jìn)行。
如下圖所示:

def filter_even(numbers):
for num in range(numbers):
if (num % 2 == 0):
yield num
even_number = filter_even(100)
print(list(even_number))
運(yùn)行結(jié)果:
$ python yielddemo.py
[0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62, 64, 66, 68, 70, 72, 74, 76, 78, 80, 82, 84, 86, 88, 90, 92, 94, 96, 98]
ayncio 版 Hello 程序
Python 3.5 版中,當(dāng)關(guān)鍵字 async 和 await 顯式添加到語(yǔ)言中時(shí),該語(yǔ)言實(shí)現(xiàn)了對(duì)協(xié)程和異步編程的一流支持。 這種語(yǔ)法在 C# 和 JavaScript 等其他編程語(yǔ)言中很常見(jiàn),它允許我們使異步代碼看起來(lái)像是同步運(yùn)行的。 這使得異步代碼易于閱讀和理解,因?yàn)樗雌饋?lái)像大多數(shù)軟件工程師熟悉的順序流程。 asyncio 是一個(gè)使用稱(chēng)為單線程事件循環(huán)的并發(fā)模型以異步方式執(zhí)行這些協(xié)程的庫(kù)。
利用 async/await 兩個(gè)定義關(guān)鍵字定義協(xié)程,通過(guò) asyncio 提供運(yùn)行和管理協(xié)程的基礎(chǔ):
import asyncio
import time
async def main():
print(f'{time.ctime()} Hello!')
await asyncio.sleep(1.0)
print(f'{time.ctime()} See you again!')
asyncio.run(main())
運(yùn)行結(jié)果:
$ python asynciodemo.py
Sat Jul 9 23:19:40 2022 Hello!
Sat Jul 9 23:19:41 2022 See you again!
asyncio 提供了一個(gè) run() 函數(shù)來(lái)執(zhí)行 async def 函數(shù),然后從那里調(diào)用的所有其他協(xié)程,如 main() 函數(shù)中的 sleep() 函數(shù)。asyncio 不是多線程或多進(jìn)程,而是并行運(yùn)行代碼。
JavaScript 中支持異步執(zhí)行(瀏覽器,Nodejs,Electron 等)。在早期版本中,他們只是使用回調(diào)功能在異步操作完成后運(yùn)行其他功能。
如何使用 asyncio
創(chuàng)建協(xié)程很簡(jiǎn)單,與創(chuàng)建普通的 Python 函數(shù)沒(méi)有太大區(qū)別。唯一的區(qū)別是,我們不是使用 def 關(guān)鍵字,而是 使用 async def 。async 關(guān)鍵字將函數(shù)標(biāo)記為協(xié)程,而不是普通的 Python 函數(shù)。
import asyncio
import time
def write(msg):
print(msg, flush=True)
async def say1():
await asyncio.sleep(1)
write("Hello from 1")
async def say2():
await asyncio.sleep(1)
write("Hello from 2")
write("start")
loop = asyncio.get_event_loop()
loop.run_until_complete(asyncio.gather(
say1(),
say2()
))
write("exit")
loop.close()
運(yùn)行該代碼,可以看到 Hello from 1 運(yùn)行 1 秒后運(yùn)行 Hello from 2:
$ python asyncoidemo2.py
start
Hello from 1
Hello from 2
exit
當(dāng) run_until_complete 運(yùn)行 say() 函數(shù),解釋器會(huì)逐行執(zhí)行該函數(shù)的內(nèi)容。當(dāng)碰到 await 之后,解釋器開(kāi)始異步操作:這個(gè)操作為了循環(huán)將完成一些內(nèi)部回調(diào)操作,這個(gè)回調(diào)操作是對(duì)開(kāi)發(fā)人員隱藏的。但是現(xiàn)在,say1 開(kāi)始后,它立即將控制返回到事件循環(huán)。所以,它啟動(dòng)異步 sleep 和控制循環(huán),然后循環(huán)實(shí)際上已經(jīng)開(kāi)始啟動(dòng) say2 函數(shù)。
當(dāng)?shù)谝淮萎惒?sleep 運(yùn)行 1秒后,進(jìn)入內(nèi)部回調(diào)執(zhí)行 say1 協(xié)程,下一個(gè)操作是打印 Hello from 1。打印后,它再次返回到活動(dòng)循環(huán)。同時(shí),從第二次睡眠開(kāi)始,循環(huán)獲得了有關(guān)完成第二次睡眠的事件。
所以接下來(lái) Hello from 2 打印,然后第二種方法也返回。
run_until_complete(gather(l1,l2,l3)) 將阻止所有 l1,l2,l3 Coroutines:

請(qǐng)注意,7 和 9 事件可能會(huì)交換 - 如果您多次運(yùn)行代碼,您可能會(huì)注意到 Hello from 1 打印在 Hello from 2 之后。
- event_loop 事件循環(huán):程序開(kāi)啟一個(gè)無(wú)限循環(huán),把一些函數(shù)注冊(cè)到事件循環(huán)上,當(dāng)滿足事件發(fā)生的時(shí)候,調(diào)用相應(yīng)的協(xié)程函數(shù)
- coroutine 協(xié)程:協(xié)程對(duì)象,指一個(gè)使用
async關(guān)鍵字定義的函數(shù),它的調(diào)用不會(huì)立即執(zhí)行函數(shù),而是會(huì)返回一個(gè)協(xié)程對(duì)象。協(xié)程對(duì)象需要注冊(cè)到事件循環(huán),由事件循環(huán)調(diào)用。 - task 任務(wù):一個(gè)協(xié)程對(duì)象就是一個(gè)原生可以掛起的函數(shù),任務(wù)則是對(duì)協(xié)程進(jìn)一步封裝,其中包含了任務(wù)的各種狀態(tài)
- future: 代表將來(lái)執(zhí)行或沒(méi)有執(zhí)行的任務(wù)的結(jié)果。它和 task 上沒(méi)有本質(zhì)上的區(qū)別
- async/await 關(guān)鍵字:python3.5 用于定義協(xié)程的關(guān)鍵字,
async定義一個(gè)協(xié)程,await用于掛起阻塞的異步調(diào)用接口。
總結(jié)
本文首先介紹了同步編程和異步編程的概念,然后引出了協(xié)程的基本概念,寫(xiě)了 asyncio 版的 HelloWorld 程序,最后給出了 Python 中 asyncio 庫(kù)的簡(jiǎn)易使用方法。
協(xié)程的優(yōu)勢(shì)在于多 IO 操作時(shí)能夠有效提高程序速度,例如某些 HTTP 客戶端,例如 aiohttps 調(diào)用服務(wù)器中就利用上了 asyncio 庫(kù)。
參考鏈接:Async IO in Python: A Complete Walkthrough
以上就是Python使用Asyncio進(jìn)行web編程方法詳解的詳細(xì)內(nèi)容,更多關(guān)于Python Asyncio web編程的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Python實(shí)現(xiàn)字典序列ChainMap
容器數(shù)據(jù)類(lèi)型包括數(shù)組list,字典dict以及元組tuple等。本篇主要介紹了ChainMap字典序列的使用,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-06-06
Python時(shí)間和日期庫(kù)的實(shí)現(xiàn)
這篇文章主要介紹了Python時(shí)間和日期庫(kù)的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-03-03
python 爬取免費(fèi)簡(jiǎn)歷模板網(wǎng)站的示例
這篇文章主要介紹了python 爬取免費(fèi)簡(jiǎn)歷模板網(wǎng)站的示例,幫助大家更好的理解和使用python 爬蟲(chóng),感興趣的朋友可以了解下2020-09-09
Python設(shè)計(jì)模式之策略模式實(shí)例詳解
這篇文章主要介紹了Python設(shè)計(jì)模式之策略模式,結(jié)合實(shí)例形式分析了策略模式的概念、原理并結(jié)合實(shí)例形式分析了Python定義與使用策略模式相關(guān)操作技巧,需要的朋友可以參考下2019-01-01
python函數(shù)和python匿名函數(shù)lambda詳解
這篇文章主要介紹了python函數(shù)和python匿名函數(shù)lambda,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-09-09
django模板結(jié)構(gòu)優(yōu)化的方法
這篇文章主要介紹了django模板結(jié)構(gòu)優(yōu)化的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-02-02
Python實(shí)現(xiàn)對(duì)二維碼數(shù)據(jù)進(jìn)行壓縮
當(dāng)前二維碼的應(yīng)用越來(lái)越廣泛,包括疫情時(shí)期的健康碼也是應(yīng)用二維碼的典型案例。本文的目標(biāo)很明確,就是使用python,實(shí)現(xiàn)一張二維碼顯示更多信息,代碼簡(jiǎn)單實(shí)用,感興趣的可以了解一下2023-02-02
Python?OpenCV中的drawMatches()關(guān)鍵匹配繪制方法
這篇文章主要介紹了Python?OpenCV中的drawMatches()關(guān)鍵匹配繪制方法,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下2022-07-07

