ジャコ Lab

プログラミング関連のメモ帳的ブログです

Python で同期処理を非同期で処理する

昨日の asyncio 続きです。

zako-lab929.hatenablog.com

振り返り

昨日は、非同期なメソッドasyncio.create_task でタスク化して並列実行しました。

import asyncio
import time

async def say_after(msg: str, delay: int) -> str:
    print(f'{delay}秒後にメッセージを出力します')
    await asyncio.sleep(delay)
    print(msg)
    return f'{delay}秒後に「{msg}」を出力しました'

async def main():
    task1 = asyncio.create_task(say_after("Hello", 10))
    task2 = asyncio.create_task(say_after("World", 5))
    a = await task1
    b = await task2
    print("Result:", a)
    print("Result:", b)

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

本題

元々同期的な処理を非同期で実施するには どうしたらよいでしょうか?
おそらく、別スレッドで処理させる必要がある と思います。

say_after が同期メソッドだった場合にどうやるのか?

asyncio でのやり方

say_after が以下のような状態のときです
import time

def say_after(msg: str, delay: int) -> str:
    print(f'{delay}秒後にメッセージを出力します')
    time.sleep(delay)
    print(msg)
    return f'{delay}秒後に「{msg}」を出力しました'
main は以下のようにすると非同期で実施できるようです
import asyncio
from datetime import datetime
from zoneinfo import ZoneInfo

async def main():
    start_datetime = datetime.now(ZoneInfo("Asia/Tokyo"))
    print(f"ENTER(main) : {start_datetime.isoformat()}")
    loop = asyncio.get_running_loop()

    task1 = loop.run_in_executor(None, say_after, "Hello", 10)
    task2 = loop.run_in_executor(None, say_after, "World", 5)
    b = await task2
    a = await task1
    print("Result:", a)
    print("Result:", b)

    end_datetime = datetime.now(ZoneInfo("Asia/Tokyo"))
    print(f"EXIT(main) : {end_datetime.isoformat()} : Elapsed Time: {round(end_datetime.timestamp() - start_datetime.timestamp(), 2)}")

if __name__ == '__main__':
    print(f"START : main()")
    asyncio.run(main())
    print(f"END : main()")

ログも出力するので非同期実施以外のコードもありますが・・・

実行結果

$ python app.py 
START : main()
ENTER(main) : 2024-09-13T22:19:54.690412+09:00
10秒後にメッセージを出力します
5秒後にメッセージを出力します
World
Hello
Result: 10秒後に「Hello」を出力しました
Result: 5秒後に「World」を出力しました
EXIT(main) : 2024-09-13T22:20:04.704484+09:00 : Elapsed Time: 10.01
END : main()
5秒後に「World」の文字が出力され、10秒後に「Hello」の文字が出力されました

time.sleep() を使っていますが、ちゃんと5秒と10秒の停止処理が非同期で動いています

まとめ

比較的簡単に非同期処理ができることがわかった