從 asyncio 開始 callback

來發個比較不廢的認真文….

這裡廢話就不多說了,簡單來說 event loop 就是你會有一個「待辦事項」(todo list),然後每次都會挑最重要的先做這樣。(heap)

白話一點(?)就是 OS 的 process scheduling (time priority),在 asyncio 裡,被排入 loop 的「待辦事項」會被放在 loop._readyloop._scheduled 裡。

概述就到這裡,接下來進主題 callback 啦。


接下來示範(?)如何把 sync function 排進 event-loop(API):

<Handle p('hello') at python:5>
deque([<Handle p('hello') at python:5>])
[<TimerHandle when=67905.932635533 p('world', True) at python:5>]
hello
world

輸出的部份會像上面↑這樣, Handle 是 loop 主要在處理的「待辦事項」,不管是之後的 Future、Task 都會再被 dispatch 到 Handle object 然後被排進 event-loop。

asyncio 提供的 calls 能使用的 callback 都是 sync function ,這部份簡單來說比較像是 javascript 的 setTimeout

有 delay call 就可以實作相關的 callback ,這邊就不舉例了,直接進到類似 Promise 的 Future 。


hello
hello world

輸出會像上面↑這樣,Future 在 loop.create_future() 之後會把 loop 綁在 fut._loop ,但 Future 並不會被排進 loop 裡。

Future 在被呼叫 .set_result(result) 之後會接著呼叫所有被加進 .add_done_callback 的 function ,並把 self 傳進第一個參數。


另一個取得 Future 物件的做法是使用 loop.run_in_executor , executor 指的是 concurrent.futures 所提供的 threading/process executor,傳入 None 的話會建一個 threading 的 executor 做為 default 使用。(ref)

delay 1 second
hello
None world

輸出會像上面↑這樣,如果用過 concurrent.futures 的話,要注意的是這裡的 Future 是被 asyncio 另外 wrap 過的,和 concurrent.Future 是不同的。

loop.run_in_executor 會直接回傳一個 Future ,所以不需要再用 loop.create_future 先建一個 Future 再傳進去,然後這裡也不需要把這個 Future 排進 loop ,因為它是跑在另一個 thread ,和 main thread 在跑的 loop 沒有關係。

最後也最重要的部份是, loop.run_in_executor 等同於跑在另一個 thread(default),和原本的持有 event_loop 的 main thread 是不同的,所以得額外注意 Lock 跟搶 GIL 的問題;在 executor 內沒有辦法用 asyncio.get_event_loop() 抓 main thread 的 loop 或是用它來建新的,如果要另外跑一個 loop 的話可以用 asyncio.new_event_loop
(但個人認為完全沒必要… loop 可以用 executor 跑 multi-thread/multi-processing ,也可以直接用 .create_subprocess_exec 跑 subprocess ,用 multi-thread 跑 multi-loop 除了自找麻煩讓腦袋燒掉以外我真的不知道能幹麻…….(別忘了 GIL)。


callback 的寫法大致上就這樣(?),雖然 Future 和 Promise 很像,但 Python 並沒有內建 Promise 或是相關的東西,用 Future 跟 callback 的方式可以實作出 Promise/A+ ,也有 3rd lib 可以參考。

run_in_executor 的用法其實比較接近 async-await 的用法了,但應該說 asyncio 本來就沒有限制(也不會限制)應該用 callback 的方式或是 async-await 的方式來寫。

最後就是個人認為(?) asyncio 跟 Python 本身其實是一樣的「易學」,只是目前還沒看到 tutorial 而已;但要強調所謂的「易學」不等同於你可以很輕易的把它寫得很漂亮,充其量就是會跑,但 performance 不知道。

如果會寫下一篇的話可能會寫 async-await 吧大概。(好懶RRRR

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.