⌛
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 を利用すればいよいはず。
-
ある処理を実行している最中は、他の処理が行えない状態 ↩︎
Discussion