歡迎您光臨本站 註冊首頁

python 異步async庫的使用說明

←手機掃碼閱讀     f2h0b53ohn @ 2020-05-05 , reply:0

在學習asyncio之前,先理清楚同步/異步的概念:
同步是指完成事務的邏輯,先執行第一個事務,如果阻塞了,會一直等待,直到這個事務完成,再執行第二個事務,順序執行
異步是和同步相對的,異步是指在處理調用這個事務的之後,不會等待這個事務的處理結果,直接處理第二個事務去了,通過狀態、通知、回調來通知調用者處理結果
asyncio函數:
異步IO採用消息循環的模式,重複“讀取消息―處理消息”的過程,也就是說異步IO模型”需要一個消息循環,在消息循環中,主線程不斷地重複“讀取消息-處理消息”這一過程。
event_loop 事件循環:程序開啟一個無限的循環,程序員會把一些函數註冊到事件循環上。當滿足事件發生的時候,調用相應的協程函數。
coroutine 協程:協程對象,指一個使用async關鍵字定義的函數,它的調用不會立即執行函數,而是會返回一個協程對象。協程對象需要註冊到事件循環,由事件循環調用。
task 任務:一個協程對象就是一個原生可以掛起的函數,任務則是對協程進一步封裝,其中包含任務的各種狀態。
async/await 關鍵字: 用於定義協程的關鍵字,async定義一個協程,await用於掛起阻塞的異步調用接口。
一、asyncio
下面通過舉例來對比同步代碼和異步代碼編寫方面的差異,其次看下兩者性能上的差距,使用asyncio.sleep(1)模擬耗時1秒的io操作。
同步代碼:
import time def hello(): time.sleep(1) def run(): for i in range(5): hello() print('Hello World:%s' % time.time()) if __name__ == '__main__': run() Hello World:1536842494.2786784 Hello World:1536842495.2796268 Hello World:1536842496.2802596 Hello World:1536842497.2804587 Hello World:1536842498.2812462
異步代碼:
import time import asyncio # 定義異步函數 async def hello(): print('Hello World:%s' % time.time()) #必須使用await,不能使用yield from;如果是使用yield from ,需要採用@asyncio.coroutine相對應 await asyncio.sleep(1) print('Hello wow World:%s' % time.time()) def run(): tasks = [] for i in range(5): tasks.append(hello()) loop.run_until_complete(asyncio.wait(tasks)) loop = asyncio.get_event_loop() if __name__ =='__main__': run() Hello World:1536855050.1950748 Hello World:1536855050.1950748 Hello World:1536855050.1950748 Hello World:1536855050.1960726 Hello World:1536855050.1960726 (暫停約1秒) Hello wow World:1536855051.1993241 Hello wow World:1536855051.1993241 Hello wow World:1536855051.1993241 Hello wow World:1536855051.1993241 Hello wow World:1536855051.1993241
async def 用來定義異步函數,其內部有異步操作。每個線程有一個事件循環,主線程調用asyncio.get_event_loop()時會創建事件循環,把異步的任務丟給這個循環的run_until_complete()方法,事件循環會安排協同程序的執行。
上述程序中,hello()會首先打印出Hello world!,然後,yield from語法可以讓我們方便地調用另一個generator。
由於await asyncio.sleep(1)也是一個coroutine,所以線程不會等待asyncio.sleep(1),而是直接中斷並執行下一個消息循環。
當asyncio.sleep(1)返回時,線程就可以從yield from拿到返回值(此處是None),然後接著執行下一行語句。
把asyncio.sleep(1)看成是一個耗時1秒的IO操作,在此期間,主線程並未等待,而是去執行EventLoop中其他可以執行的coroutine了,因此可以實現併發執行。
asyncio操作的總結:
async def hello(): 定義async異步函數,中間可以添加await async.sleep(N) 來設定中斷並執行下一個循環消息
tasks = [] 任務則是對協程進一步封裝,其中包含任務的各種狀態。即多個coroutine函數可以封裝成一組Task然後併發執行
loop = asyncio.get_event_loop() #獲取“事件循環”對象
loop.run_until_complete(asyncio.wait(tasks)) #通過事件循環,去調用協程函數
loop.close() 結束時間循環
二、aiohttp
如果需要併發http請求,通常是用requests,但requests是同步的庫,如果想異步的話需要引入aiohttp。
這裡引入一個類,from aiohttp import ClientSession,首先要建立一個session對象,然後用session對象去打開網頁。
session可以進行多項操作,比如post, get, put, head等。
基本用法:
async with ClientSession() as session: async with session.get(url) as response:
aiohttp異步實現的例子:
import asyncio from aiohttp import ClientSession tasks = [] url = "https://www.baidu.com/{}" async def hello(url): async with ClientSession() as session: async with session.get(url) as response: response = await response.read() print(response) if __name__ == '__main__': loop = asyncio.get_event_loop() loop.run_until_complete(hello(url))
首先async def 關鍵字定義了這是個異步函數,await 關鍵字加在需要等待的操作前面,response.read()等待request響應,是個耗IO操作。然後使用ClientSession類發起http請求。
多鏈接異步訪問
如果我們需要請求多個URL該怎麼辦呢,同步的做法訪問多個URL只需要加個for循環就可以了。但異步的實現方式並沒那麼容易,在之前的基礎上需要將hello()包裝在asyncio的Future對象中,然後將Future對象列表作為任務傳遞給事件循環。
import time import asyncio from aiohttp import ClientSession tasks = [] url = "https://www.baidu.com/{}" async def hello(url): async with ClientSession() as session: async with session.get(url) as response: response = await response.read() print('Hello World:%s' % time.time()) def run(): for i in range(5): task = asyncio.ensure_future(hello(url.format(i))) tasks.append(task) if __name__ == '__main__': loop = asyncio.get_event_loop() run() loop.run_until_complete(asyncio.wait(tasks)) Hello World:1536843566.064149 Hello World:1536843566.070586 Hello World:1536843566.0769563 Hello World:1536843566.0779328 Hello World:1536843566.0799286
・收集http響應
好了,上面介紹了訪問不同鏈接的異步實現方式,但是我們只是發出了請求,如果要把響應一一收集到一個列表中,最後保存到本地或者打印出來要怎麼實現呢,可通過asyncio.gather(*tasks)將響應全部收集起來
import time import asyncio from aiohttp import ClientSession tasks = [] url = "https://www.baidu.com/{}" async def hello(url): async with ClientSession() as session: async with session.get(url) as response: # print(response) print('Hello World:%s' % time.time()) return await response.read() def run(): for i in range(5): task = asyncio.ensure_future(hello(url.format(i))) tasks.append(task) result = loop.run_until_complete(asyncio.gather(*tasks)) print(result) if __name__ == '__main__': loop = asyncio.get_event_loop() run() Hello World:1536843488.678779 Hello World:1536843488.6797836 Hello World:1536843488.6867576 Hello World:1536843488.6877556 Hello World:1536843488.6877556


[f2h0b53ohn ] python 異步async庫的使用說明已經有307次圍觀

http://coctec.com/docs/python/shhow-post-232924.html