asyncio(非同步io)
基本概念
引入非同步
-
同步、非同步(執行緒間)
所謂同步,就是發出一個功能的呼叫時,在沒有得到結果之前,該呼叫就不返回或繼續執行後續操作。簡單來說,同步就是必須一件一件事做,等前一件做完了才能做下一件事
同步與非同步相對,當一個非同步呼叫發出後,呼叫者在沒有得到結果之前,就可以繼續執行後續操作。當這個呼叫完成後,一般通過狀態、通知和回撥來通知呼叫者。對於非同步呼叫,呼叫的返回並不受呼叫者控制。通知呼叫者的三種方式,具體如下:方式 描述 狀態 即監聽被呼叫者的狀態(輪詢),呼叫者每個一定時間檢查一次,效率很低 通知 當被呼叫者執行完成後,發出通知告知被呼叫者,無需消耗太多效能 回撥 與通知類似,當被呼叫者執行完成後,會呼叫呼叫者提供的回撥函式 -
阻塞、非阻塞(執行緒內)
阻塞和非阻塞兩個概念僅僅與等待訊息通知的狀態相關。跟同步、非同步沒有太大關係,也就是說阻塞與非阻塞主要是程式等待通知時的狀態來講的
阻塞呼叫是指呼叫結果返回之前,當前執行緒會被掛起。呼叫執行緒只有在得到結果後才會返回
非阻塞是指在不能立刻得到結果之前,該呼叫不會阻塞當前執行緒 -
組合
式 描述 同步阻塞 傳送方請求之後一直等待響應。接收方處理請求時進行的IO操作如果不能等到返回結果,就一直等到返回結果,才響應傳送方 同步非阻塞 傳送方傳送請求之後一直等待。接收方處理請求時進行的IO操作如果不能得到結果,就立即返回,去做其他事。但是由於沒有得到請求結果,不響應傳送方,傳送方一直等待。當IO操作完成以後,將完成狀態和結果通知接收方,接收方響應傳送方,傳送方進入下一次請求過程 非同步阻塞 傳送方向接收方請求後,不等待響應,可以繼續其他工作。接收方處理請求時進行IO操作如果不能馬上得到結果,就一直等到返回結果後才響應傳送方 非同步非阻塞 傳送方向接收方傳送請求後,不等待響應,可以繼續其他工作。接收方處理請求時進行IO操作如果不能得到結果,也不等待,而是馬上返回去做其他的事。當IO操作完成後,將完成狀態和結果通知接收方,接收方在響應傳送方
引入協程
- 協程
協程,又稱微執行緒,英文名Coroutine。 - 子程式
在所有語言中都是層級呼叫,比如A呼叫B,B在執行過程中又呼叫了C,C執行完畢返回,B執行完畢返回,最後是A執行完畢。所以子程式呼叫是通過棧實現的,一個執行緒就是執行一個子程式。子程式呼叫總是一個入口,一次返回,呼叫順序是明確的。 - 多執行緒
避免順序執行的方式之一是多執行緒,但是考慮到python語言的特性(GIL鎖),再執行計算密集型的任務時,多執行緒的執行效果反而變慢,再執行IO密集型的任務時候雖然有不錯的效能提升,但是依然會有執行緒管理與切換、同步的開銷等等(具體原因這裡不詳細說明,請參見相關的GIL說明) - 協程優勢
- 最大的優勢就是協程極高的執行效率。因為子程式切換不是執行緒切換,而是由程式自身控制,因此,沒有執行緒切換的開銷,和多執行緒比,執行緒數量越多,協程的效能優勢就越明顯
- 就是不需要多執行緒的鎖機制,因為只有一個執行緒,也不存在同時寫變數衝突,在協程中控制共享資源不加鎖,只需要判斷狀態就好了,所以執行效率比多執行緒高很多
asyncio概念與框架
- 什麼是task任務
Task用來併發排程的協程,即對協程函式的進一步包裝?那為什麼還需要包裝呢?因為單純的協程函式僅僅是一個函式而已,將其包裝成任務,任務是可以包含各種狀態的,非同步程式設計最重要的就是對非同步操作狀態的把控了。
-
建立任務
task = asyncio.create_task(coro()) ## task = asyncio.ensure_future(coro()) ## loop.create_future(coro()) # loop為迴圈物件 ## loop.create_task(coro())
-
獲取某一任務
task=asyncio.current_task(loop=None) ## 返回正在執行的任務 asyncio.all_tasks(loop=None) ## 返回還沒有結束的任務
-
future物件
Future是一個較低層的可等待(awaitable)物件,他表示的是非同步操作的最終結果,當一個Future物件被等待的時候,協程會一直等待,直到Future已經運算完畢。
Future是Task的父類,一般情況下,已不用去管它們兩者的詳細區別,也沒有必要去用Future,用Task就可以了,返回 future 物件的低階函式的一個很好的例子是 loop.run_in_executor(). -
非同步函式獲取結果
-
直接通過result獲取
import asyncio import time async def hello1(a,b): print("hello world 01 begin") await asyncio.sleep(3) print("hello again 01 end") return a+b coroutine = hello1(10,5) loop = asyncio.get_event_loop() task = asyncio.ensure_future(coroutine) loop.run_until_complete(task) print('--------------------------') print(task.result()) loop.close()
-
通過呼叫回撥函式
import asyncio import time async def hello1(a,b): print("hello world 01 begin") await asyncio.sleep(3) print("hello again 01 end") return a+b def callback(future): print(future.result()) coroutine = hello1(10,5) loop = asyncio.get_event_loop() task = asyncio.ensure_future(coroutine) task.add_done_callback(callback) loop.run_until_complete(task) loop.close()
- 模板(3.7之前)
-
無參無返回值
import asyncio async def hello1(): print("hello world 01 begin") await asyncio.sleep(3) print("hello again 01 end") async def hello2(): print("hello world 02 begin") await asyncio.sleep(2) print("hello again 02 end") async def hello3(): print("hello world 03 begin") await asyncio.sleep(1) print("hello again 03 end") def callback(future): print(future.result()) loop = asyncio.get_event_loop() ## 建立事件迴圈 tasks = [hello1(),hello2(),hello3()] ## 將多個協程函式包裝成任務列表 loop.run_until_complete(asyncio.wait(tasks))## 通過事件迴圈執行 loop.close()## 取消事件迴圈
-
有參有返回值
import asyncio async def hello1(a,b): print("hello world 01 begin") await asyncio.sleep(3) print("hello again 01 end") return a+b async def hello2(a,b): print("hello world 02 begin") await asyncio.sleep(2) print("hello again 02 end") return a-b async def hello3(a,b): print("hello world 03 begin") await asyncio.sleep(1) print("hello again 03 end") return a*b loop = asyncio.get_event_loop() ## 建立事件迴圈 task1 = asyncio.ensure_future(hello1(10,2)) task2 = asyncio.ensure_future(hello2(19,2)) task3= asyncio.ensure_future(hello3(9,2)) tasks = [task1,task2,task3] loop.run_until_complete(asyncio.wait(tasks))## 通過事件迴圈執行 print(task1.result()) print(task2.result()) print(task3.result()) loop.close()## 取消事件迴圈
4.1. 流程
-
構造事件迴圈
loop=asyncio.get_running_loop() #返回(獲取)在當前執行緒中正在執行的事件迴圈,如果沒有正在執行的事件迴圈,則會顯示錯誤;它是python3.7中新新增的 loop=asyncio.get_event_loop() #獲得一個事件迴圈,如果當前執行緒還沒有事件迴圈,則建立一個新的事件迴圈loop; loop=asyncio.set_event_loop(loop) #設定一個事件迴圈為當前執行緒的事件迴圈; loop=asyncio.new_event_loop() #建立一個新的事件迴圈
-
包裝task
task = asyncio.create_task(coro(引數列表)) # 這是3.7版本新新增的 task = asyncio.ensure_future(coro(引數列表))
-
執行
loop.run_until_complete(asyncio.wait(tasks)) #通過asyncio.wait()整合多個task loop.run_until_complete(asyncio.gather(tasks)) #通過asyncio.gather()整合多個task loop.run_until_complete(task_1) #單個任務則不需要整合 loop.run_forever() #但是這個方法在新版本已經取消,不再推薦使用,因為使用起來不簡潔 使用gather或者wait可以同時註冊多個任務,實現併發,但他們的設計是完全不一樣的,主要區別如下: (1)引數形式不一樣 gather的引數為 *coroutines_or_futures,即如這種形式 tasks = asyncio.gather(*[task1,task2,task3])或者 tasks = asyncio.gather(task1,task2,task3) loop.run_until_complete(tasks) wait的引數為列表或者集合的形式,如下 tasks = asyncio.wait([task1,task2,task3]) loop.run_until_complete(tasks) (2)返回的值不一樣 gather的定義如下,gather返回的是每一個任務執行的結果, results = await asyncio.gather(*tasks) wait的定義如下,返回dones是已經完成的任務,pending是未完成的任務,都是集合型別 done, pending = yield from asyncio.wait(fs) (3)後面還會講到他們的進一步使用
-
關閉
loop.close()
- 模板(3.7之後)
-
無參無返回值
import asyncio import time async def hello1(): print("Hello world 01 begin") await asyncio.sleep(3) #模擬耗時任務3秒 print("Hello again 01 end") async def hello2(): print("Hello world 02 begin") await asyncio.sleep(2) #模擬耗時任務2秒 print("Hello again 02 end") async def hello3(): print("Hello world 03 begin") await asyncio.sleep(4) #模擬耗時任務4秒 print("Hello again 03 end") async def main(): results=await asyncio.gather(hello1(),hello2(),hello3()) for result in results: print(result) #因為沒返回值,故而返回None asyncio.run(main())
-
有參有返回值
import asyncio import time async def hello1(a,b): print("Hello world 01 begin") await asyncio.sleep(3) #模擬耗時任務3秒 print("Hello again 01 end") return a+b async def hello2(a,b): print("Hello world 02 begin") await asyncio.sleep(2) #模擬耗時任務2秒 print("Hello again 02 end") return a-b async def hello3(a,b): print("Hello world 03 begin") await asyncio.sleep(4) #模擬耗時任務4秒 print("Hello again 03 end") return a*b async def main(): results=await asyncio.gather(hello1(10,5),hello2(10,5),hello3(10,5)) for result in results: print(result) asyncio.run(main())
- gather 與 wait區別
-
gather
import asyncio from pprint import pprint import random async def coro(tag): print(">", tag) await asyncio.sleep(random.uniform(1, 3)) print("<", tag) return tag loop = asyncio.get_event_loop() group1 = asyncio.gather(*[coro("group 1.{}".format(i)) for i in range(1, 6)]) group2 = asyncio.gather(*[coro("group 2.{}".format(i)) for i in range(1, 4)]) group3 = asyncio.gather(*[coro("group 3.{}".format(i)) for i in range(1, 10)]) all_groups = asyncio.gather(group1, group2, group3) results = loop.run_until_complete(all_groups) loop.close() pprint(results)
組中的所有任務都可以通過呼叫group2.cancel()甚至all_groups.cancel()…。
-
wait
import asyncio import random async def coro(tag): print(">", tag) await asyncio.sleep(random.uniform(0.5, 5)) print("<", tag) return tag loop = asyncio.get_event_loop() tasks = [coro(i) for i in range(1, 11)] print("Get first result:") finished, unfinished = loop.run_until_complete( asyncio.wait(tasks, return_when=asyncio.FIRST_COMPLETED)) for task in finished: print(task.result()) print("unfinished:", len(unfinished)) print("Get more results in 2 seconds:") finished2, unfinished2 = loop.run_until_complete( asyncio.wait(unfinished, timeout=2)) for task in finished2: print(task.result()) print("unfinished2:", len(unfinished2)) print("Get all other results:") finished3, unfinished3 = loop.run_until_complete(asyncio.wait(unfinished2)) for task in finished3: print(task.result()) loop.close()
支援在完成第一個任務後或在指定的超時之後等待停止,從而允許操作的精度降低:
相關文章
- asyncio非同步IO——Streams詳解非同步
- Python 非同步 IO系列:認識asyncioPython非同步
- 【python】非同步IO | 協程 | asyncio | await | yieldPython非同步AI
- java同步非阻塞IOJava
- Java 非阻塞 IO 和非同步 IOJava非同步
- IO模式和IO多路複用(阻塞IO、非阻塞IO、同步IO、非同步IO等概念)模式非同步
- IO - 同步 非同步 阻塞 非阻塞的區別非同步
- IO通訊模型(二)同步非阻塞模式NIO(NonBlocking IO)模型模式BloC
- Python非同步IO程式設計之-asyncio協程應用例子Python非同步程式設計
- 手把手教你如何使用Python的非同步IO框架:asyncio(下)Python非同步框架
- 手把手教你如何使用Python的非同步IO框架:asyncio(中)Python非同步框架
- 手把手教你如何使用Python的非同步IO框架:asyncio(上)Python非同步框架
- python之IO併發-阻塞IO 非阻塞IO IO多路複用 非同步IO(協程)Python非同步
- 談談對不同I/O模型的理解 (阻塞/非阻塞IO,同步/非同步IO)模型非同步
- 如何解讀 Java IO、NIO 中的同步阻塞與同步非阻塞?Java
- asyncio 非同步程式設計非同步程式設計
- 11、協程和io教程01 -- 併發 並行 同步 非同步 阻塞 非阻塞 以及 IO多路複用並行非同步
- 【死磕NIO】— 阻塞IO,非阻塞IO,IO複用,訊號驅動IO,非同步IO,這你真的分的清楚嗎?非同步
- 阻塞IO與非阻塞IO
- Java網路程式設計和NIO詳解5:Java 非阻塞 IO 和非同步 IOJava程式設計非同步
- 如何給女朋友解釋什麼是IO中的阻塞、非阻塞、同步、非同步?非同步
- python非同步asyncio模組的使用Python非同步
- Python非同步協程(asyncio詳解)Python非同步
- 簡述python非同步i/o庫 —— asyncioPython非同步
- 同步非同步,阻塞非阻塞非同步
- 非同步、同步、阻塞、非阻塞非同步
- [作業系統]阻塞io 非阻塞io Epoll作業系統
- FastAPI之阻塞式io和非阻塞式ioASTAPI
- 基於asyncio、aiohttp、xpath的非同步爬蟲AIHTTP非同步爬蟲
- python非同步程式設計之asyncio初識Python非同步程式設計
- asyncio非同步程式設計【含視訊教程】非同步程式設計
- 深入理解 python 非同步 i/o 庫 —— asyncioPython非同步
- 同步、非同步,阻塞、非阻塞理解非同步
- 同步、非同步、阻塞與非阻塞非同步
- 同步非同步 與 阻塞非阻塞非同步
- 理解阻塞、非阻塞、同步、非同步非同步
- [python] Python非同步程式設計庫asyncio使用指北Python非同步程式設計
- Python的非同步IO:APIPython非同步API