2017年4月29日 星期六

python asyncio 入門介紹

asyncio 用在有些操作需要 block ,但是你不想要讓 main thread 被 block 時。你可以透過 multi thread 或是 multiprocessing 來解決剛剛的問題,但是建立 thread 或 process 的成本比較高,切換 thread 或 process 需要 kernel 介入,速度慢等問題。而 asyncio 無此問題。

點選文章閱讀全文

asyncio 觀念很簡單,會有一個 thread 去跑所有的 work,在此叫他 event loop(在此用 work 而不用 task 的原因是,task 是 asynio 裡的一個類別,為了避免混淆,所以用 work)。 work 是透過呼叫 "async def" 的 function 所建立的,event loop 一次只會執行一個 work。當 event loop 執行 work 時,遇到 await 時,就會讓其他 work 先執行。等到他 await 的東西好了,該 work 會重新進入排程,等待被 event loop 執行。
import asyncio

from concurrent.futures import ThreadPoolExecutor
import random
import time

io_pool_exc = ThreadPoolExecutor()

async def openfile(name):
    sleep_time = random.randint(1, 5)
    with open(__file__, 'r') as f:
        await asyncio.sleep(sleep_time)
        content = await loop.run_in_executor(io_pool_exc, f.readlines)
        print(name)
    return sleep_time


async def test():
    start = time.time()
    future = asyncio.gather(
            openfile('a.py'),
            openfile('b.py'),
            openfile('c.py'),

    )
    print('Jobs had scheduled')
    await future
    done = time.time()
    elapsed = done - start

    print("total sleep time: %d %s" % (sum(future.result()), future.result()))
    print("elapsed time: %d" % elapsed)
    print('jobs had done')
    loop.stop()

loop = asyncio.get_event_loop()
asyncio.ensure_future(test())
loop.run_forever()
loop.close()


以上面的程式碼為例子
如果沒有 asyncio ,這程式的執行時間會是
"a.py" 的 sleep time 加上 "b.py" 的 sleep time 加上 "c.py" 的 sleep time 加上
 不過這個程式執行時間只有那三個執行中,最長的那一個
因為 asyncio 在執行時,遇到 block 的東西時,不會傻傻地等
就會開始執行其他 work

不過有個問題,asyncio 遇到 block 的東西就會執行其他 work
可是 block 的東西也需要被執行阿
一般的作法,就是建立新的 thread 來執行會 block 的東西
(當然找個支援 asyncio 的 library 也可以)
以上面的例子來說
會 block 的操作都被丟到一個名子叫 io_pool_exc 的 ThreadPoolExecutor 。
把會 block 的操作都丟到 ThreadPoolExecutor 有下列好處
  1. event loop 就不會被 block 了
  2. 你可以控制 thread 的最大數量
  3. 你可以重複使用既有的 thread,而非重複建立刪除 thread



在 gist 上的程式碼
https://gist.github.com/ya790206/a5ae684c6859aad07cb8ab25be6b4c1b

沒有留言:

張貼留言