⌛

PythonでasyncとThredPoolExcutorを比べてみた

に公開

概要

Pythonの非同期処理ライブラリ async と 並行処理ライブラリ ThredPoolExcutor の違いがよくわからなかったので、テストコードを作成して、使い方を考えてみました。

結論

非同期処理 async

async では、 await を実行することでCPUの処理切り替えが発生する。一方で await を実行するには、行いたい処理が async に対応している必要がある。そのため PostgreSQL への書き込みを async で行う場合には、asyncpg の利用が必須となる。このように async に対応した処理の場合には、async による非同期処理が実装可能となる。

並行処理 ThredPoolExcutor

並行処理 ThredPoolExcutor を使う場面は、async に対応していない処理や、コードできるだけ変えずに並行化したい場合が考えられる。

テストコード

非同期処理 async

import asyncio
import time

from icecream import ic


async def io_task(name):
    print(f"{name} start")
    await asyncio.sleep(3)  # ノンブロッキングI/O待ち
    print(f"{name} end")


async def main():
    start = time.time()
    await asyncio.gather(io_task("A"), io_task("B"), io_task("C"))
    print(f"elapsed: {time.time() - start:.3f}")


ic()
asyncio.run(main())
ic()
# 実行結果
ic| main_async.py:19 in <module> at 22:06:11.408
A start
B start
C start
A end
B end
C end
elapsed: 3.001
ic| main_async.py:21 in <module> at 22:06:14.412

並行処理 ThredPoolExcutor

import time
from concurrent.futures import ThreadPoolExecutor

from icecream import ic


def io_task(name):
    print(f"{name} start")
    time.sleep(3)  # ブロッキングI/O
    print(f"{name} end")


ic()
start = time.time()
with ThreadPoolExecutor(max_workers=3) as executor:
    executor.submit(io_task, "A")
    executor.submit(io_task, "B")
    executor.submit(io_task, "C")

print(f"elapsed: {time.time() - start:.3f}")
# 実行結果
ic| main_ThreadPoolExcutor.py:13 in <module> at 22:07:03.543
A start
B start
C start
C end
B end
A end
elapsed: 3.003

まとめ

time.sleep のようにブロッキング処理[1]が必要な場合には、ThredPoolExcutor を使用し、 asyncio.sleep のようなノンブロッキング処理が必要な場合には、async を利用すればいよいはず。

脚注
  1. ある処理を実行している最中は、他の処理が行えない状態 ↩︎

Discussion