-
asynchronous(異步)/concurrent(併發)/multiprocess(多進程) in Python
相關模塊有:
_thread
threading
(對_thread的高級封裝)multiprocessing
concurrent.futures
(多threading/multiprocessing的高級封裝)asyncio
-
asyncio
參考資料:
asyncio is a library to write concurrent(併發) code using the async/await syntax.
asyncio is used as a foundation for multiple Python asynchronous(異步) frameworks that provide high-performance network and web-servers, database connection libraries, distributed task queues, etc.
asyncio模塊有四個基本概念:
- eventloop
- coroutine
- future
- task
Python3.4 增加了asyncio模塊支持異步IO,起初使用
@asyncio.coroutine || yield from
語法,Python3.5後使用async || await
語法替代。 -
異步IO asynchronous|| 併發 concurrency || 並行 parallel1
所謂異步IO,就是當發起一個IO操作,卻不等它結束,直接去做其他事情,當它結束時,會通知你。
同步IO模型的代碼無法實現異步IO模型。異步IO模型需要有一個eventloop,在eventloop中主線程不斷重複“讀取消息–> 處理消息”這一過程。
所謂併發(concurrency),是指同一時刻平等的運行不同事情。多進程、多線程都可以實現。是一種邏輯上的假相。不同任務邏輯上同時運行,但處理器只有一個,同一時刻處理器上只能處理一個任務。核心是交替,邏輯上。
所謂並行(parallel),是真正的同時運行,同一時刻多個任務在多個處理器上同時執行。核心是並列,物理上。
併發指的是程序的結構,並行指的是程序運行時的狀態。
-
概念一:事件循環 event loop
功能類似於CPU(順序執行協程代碼)、操作系統(完成協程調度)。
作爲中央總控,eventloop實例提供了註冊/取消/執行任務(和回調)的方法。
把一些列異步函數註冊到事件循環上,事件循環就會循環執行這些函數(同一時刻只能執行一個),當執行到某個函數正在等待I/O返回,事件循環就會暫停它的執行去執行其他函數;當某個函數完成I/O後會恢復,下次循環到它的時候繼續執行。
eventloop機理上,遇到IO操作時,代碼只發出IO請求,不等待IO結果,直接結束本輪消息處理,進入下一輪消息處理過程。
當IO操作完成後,收到一條“IO完成”的消息,處理該消息時就可以直接獲取IO操作結果。
在“發出IO請求”到收到“IO完成”這段時間內,主線程繼續處理eventloop中其他消息,如此異步IO模型下,一個線程就可以同時處理多個IO請求,而不用產生線程切換的消耗。
The eventloop is what schedules and runs asynchronous tasks.
-
uvloop
According to the authors of uvloop, it is comparible in speed to that of Go programs.
If you want to utilize uvloop the first install it ,the add a call to
uvloop.install()
import asyncio import uvloop async def foo(): print("Foo!") async def hello_world(): await foo() print("Hello World!") uvloop.install() # 使用uvloop替換原生eventloop asyncio.run(hello_world())
-
概念二:協程 coroutine
coroutine本質上是一個函數,在代碼塊中可以由用戶將執行權交給其他協程。
協程可以做哪些事2:
- 等待一個future結束
- 等待另一個協程(產生一個結果,或引發一個異常) await asyncio.sleep(x)
- 產生一個結果給正在等它的協程
- 引發一個異常給正在等它的協程
-
function or object
- coroutine function : an async def function;
- coroutine object : an object returned by calling a coroutine function
-
協程的運行
調用協程函數,協程並不會開始運行,只是返回一個協程對象。
運行協程對象的兩種方式:
- 在另一個已經運行的協程中用“await”等待它
- 通過“ensure_future”函數計劃它的執行
本質上,只有loop運行了,協程纔會運行。
import asyncio async def do_some_work(x): # async 用於定義一個coroutine對象 print("waiting " + str(x)) await asyncio.sleep(x) # await調用另一個coroutine對象 print("again") # asycnio.sleep()相當於IO,這裏並不是等待IO結束,而是直接中斷do_some_work()這個coroutine,當IO完成後才重新啓動本協程 loop = asyncio.get_event_loop() # 定義一個無協程的loop loop.run_until_complete(do_some_work(3)) # 把協程對象加入到loop,此處參數應爲future對象,只是內部可以自動檢測封裝 loop.run_until_complete(asyncio.ensure_future(do_some_work(3))) # 正規寫法
run_until_complete只接收單個future,如果有多個協程需要用syncio.gather([coroutine1,c2,…]),asyncio.gather()本質是併發運行任務.
loop = asyncio.get_event_loop() loop.run_until_complete(main()) loop.close() # 等價於 asyncio.run(main())
The
.run
function always creates a new event loop and closes it at the end. It’s a high-level API. -
協程的併發:asyncio.gather || asyncio.wait
return_a, return_b = await asyncio.gather(a(), b()) done, pending = await asyncio.wait([a(), b()]) # return_a 等價於list(done)[0].result()
兩者都是讓多個協程併發執行,
asyncio.gather()
會按照輸入協程的順序保存對應協程的執行結果。asyncio.wait()
返回封裝的task
(已完成的和被掛起的任務),如果需要協程的執行結果就要從對應task實例中採用result方法拿到。asyncio.wait()
接收一個參數return_when
,表示返回的時機。可選有:ALL_COMPLETED
: 全部任務完成後返回FIRST_COMPLETED
: 第一個協程完成後返回FIRST_EXCEPTION
: 第一個異常出現後返回
-
協程與函數
在函數世界內,當一個函數中斷去執行另一個函數採用調用另一個函數;協程中,函數A執行中斷去執行函數B是由事件循環操作的,並不涉及函數之間的調用。
不同協程是在同一個線程內切換的。
-
概念三:Future
future是coroutine的封裝,future對象提供了很多任務方法,可以向Future實例添加完成後的回調(add_done_callback)、取消任務(cancle)、設置最終結果(set_result)、設置一場(set_exception)等。
-
概念四:Task
task是future這種底層類的子類。實際使用中更過直接調用task而非future。
Task的創建有三種方法:
- asyncio.create_task(coro) Python3.7後推薦用法,本質是對loop.create_task的封裝
- loop.create_task(coro)
- asyncio,ensure_future(coro/future/awaitable)
-
Awaitables
Something is awaitable if it can be used in an await expression.
There are three main tyoes of awaitables:
- Coroutine
- Tasks
- Futures(low-level type)
-
回調
可以給
Task/Future
添加回調函數,等task完成後就會自動調用這些回調函數。task.add_done_callback(callback)
回調函數按其註冊順序被調用。
-
同步代碼
如果有同步邏輯需要執行,可以放在
loop.run_in_executor(concurrent.futures.Executor實例,同步函數)
裏執行。 -
References
第三次理解asyncio
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.