Pythonでの並列処理・非同期処理の使い分け
Pythonには並列処理の方法が複数あり、用途によって使い分けが必要です。 GIL(グローバルインタープリタロック)の存在により、 CPUバウンドとI/Oバウンドで最適な手法が異なります。
使い分けの基本方針
- I/Oバウンド(API呼び出し、DB、ファイル): asyncio または threading
- CPUバウンド(計算処理、画像処理): multiprocessing
asyncio(非同期I/O)
import asyncio
import aiohttp
async def fetch(url: str) -> str:
async with aiohttp.ClientSession() as session:
async with session.get(url) as resp:
return await resp.text()
async def main():
urls = ["https://api.example.com/1", "https://api.example.com/2"]
results = await asyncio.gather(*[fetch(u) for u in urls])
return results
asyncio.run(main())
ThreadPoolExecutor(マルチスレッド)
from concurrent.futures import ThreadPoolExecutor
import requests
def fetch(url):
return requests.get(url).text
urls = ["https://example.com/1", "https://example.com/2"]
with ThreadPoolExecutor(max_workers=10) as executor:
results = list(executor.map(fetch, urls))
ProcessPoolExecutor(マルチプロセス)
from concurrent.futures import ProcessPoolExecutor
def heavy_compute(n):
return sum(i ** 2 for i in range(n))
with ProcessPoolExecutor() as executor:
results = list(executor.map(heavy_compute, [10**6, 10**6, 10**6]))
まとめ
スクレイピングやAPI並列呼び出しにはasyncioが最も効率的です。 GILの影響を受けるCPU処理はmultiprocessingで別プロセスとして実行しましょう。