仙豆のレシピ

ちょっとしたことでも書いていく姿勢で

asyncio 理解した気になる方法メモ

python の asyncio について。 きっちり理解した方がいいんだろうけどとりあえずお手軽に理解した気になれると(困らない限り)便利なので書いておく。

基本コード

関数 funcA()funcB() を非同期に実行する。 前者は2秒間隔で A1 ~ A3 を出力、後者は1秒間隔で B1 ~ B5 を出力する。

実行コード

import time
import asyncio
from datetime import datetime, timedelta
from typing import Callable


async def funcA(current_fn: Callable):
    print(current_fn(), 'A1')
    await asyncio.sleep(2)
    print(current_fn(), 'A2')
    await asyncio.sleep(2)
    print(current_fn(), 'A3')


async def funcB(current_fn: Callable):
    print(current_fn(), 'B1')
    await asyncio.sleep(1)
    print(current_fn(), 'B2')
    await asyncio.sleep(1)
    print(current_fn(), 'B3')
    await asyncio.sleep(1)
    print(current_fn(), 'B4')
    await asyncio.sleep(1)
    print(current_fn(), 'B5')


async def main():
    s = int(datetime.now().strftime('%S'))
    def current() -> str:
        t = datetime.now() + timedelta(seconds=(60-s))
        return t.strftime('%S (%f)')

    await asyncio.gather(
        funcA(current),
        funcB(current),
    )


if __name__ == '__main__':
    asyncio.run(main())

以下すべて python3.8.10 で試した。

1)全部 asyncio.sleep()

こんな感じの出力になる(カッコ内はマイクロ秒):

00 (851502) A1
00 (851523) B1
01 (853204) B2
02 (852528) A2
02 (853741) B3
03 (855156) B4
04 (854765) A3
04 (856003) B5

たぶんイメージはこんな感じ:

await してる間は別の処理ができる、という認識でよさそう? (ちなみに B1 ~ B2 間を time.sleep() にしても同じ結果)

2)A2 ~ A3 間を time.sleep() にする

async def funcA(current_fn: Callable):
    print(current_fn(), 'A1')
    await asyncio.sleep(2)
    print(current_fn(), 'A2')
-    await asyncio.sleep(2)
+    time.sleep(2)
    print(current_fn(), 'A3')

出力はこうなる:

00 (961069) A1
00 (961089) B1
01 (962646) B2
02 (963158) A2
04 (965308) A3
04 (965432) B3
05 (966404) B4
06 (967694) B5

イメージ:

A2 ~ A3 間は他の処理がブロックされる。

3)B2 ~ B3 間を time.sleep() にする

async def funcB(current_fn: Callable):
    print(current_fn(), 'B1')
    await asyncio.sleep(1)
    print(current_fn(), 'B2')
-    await asyncio.sleep(1)
+    time.sleep(1)
    print(current_fn(), 'B3')
    await asyncio.sleep(1)
    print(current_fn(), 'B4')
    await asyncio.sleep(1)
    print(current_fn(), 'B5')

出力:

00 (949751) A1
00 (949771) B1
01 (951187) B2
02 (952380) B3
02 (952671) A2
03 (953892) B4
04 (954185) A3
04 (954274) B5

ここまでの感じのイメージだと:

となって B5 の後に A3 が出力される気がするが、実際に試すと A3 が先になる。 これは B3 ~ B5 間の処理のオーバーヘッドが B3A2 の間隔より大きくなってしまう、みたいな理解でいいんだろうか。