並行処理
GIL(Global Interpreter Lock)
Python(CPython)にはGILがあり、同時に1つのスレッドしかPythonコードを実行できません。
- I/O待ち処理: スレッドが効果的(ネットワーク、ファイルI/O)
- CPU集約処理: マルチプロセスが効果的
threading(マルチスレッド)
import threading
import time
def download(url, result, index):
print(f"ダウンロード開始: {url}")
time.sleep(2) # I/O待ちをシミュレート
result[index] = f"{url} の内容"
print(f"ダウンロード完了: {url}")
urls = ['url1', 'url2', 'url3', 'url4']
results = [None] * len(urls)
threads = []
for i, url in enumerate(urls):
t = threading.Thread(target=download, args=(url, results, i))
threads.append(t)
t.start()
for t in threads:
t.join() # 全スレッドの完了を待つ
print(results)
ThreadPoolExecutor
from concurrent.futures import ThreadPoolExecutor, as_completed
import time
def fetch_data(url):
time.sleep(1)
return f"{url} のデータ"
urls = [f'https://api.example.com/data/{i}' for i in range(10)]
with ThreadPoolExecutor(max_workers=5) as executor:
futures = {executor.submit(fetch_data, url): url for url in urls}
for future in as_completed(futures):
url = futures[future]
result = future.result()
print(f"{url}: {result}")
multiprocessing(マルチプロセス)
from multiprocessing import Pool
import time
def heavy_computation(n):
total = sum(i * i for i in range(n))
return total
if __name__ == '__main__':
numbers = [10_000_000] * 4
# マルチプロセス
start = time.perf_counter()
with Pool(4) as pool:
results = pool.map(heavy_computation, numbers)
print(f"マルチプロセス: {time.perf_counter() - start:.2f}秒")
# シングルプロセス
start = time.perf_counter()
results = [heavy_computation(n) for n in numbers]
print(f"シングルプロセス: {time.perf_counter() - start:.2f}秒")
asyncio(非同期I/O)
import asyncio
async def fetch_data(url, delay):
print(f"取得開始: {url}")
await asyncio.sleep(delay) # I/O待ちをシミュレート
print(f"取得完了: {url}")
return f"{url} のデータ"
async def main():
# 並行実行
tasks = [
fetch_data('url1', 2),
fetch_data('url2', 1),
fetch_data('url3', 3),
]
results = await asyncio.gather(*tasks)
print(results)
asyncio.run(main())
# 合計6秒ではなく最大3秒で完了
使い分け
| 種類 | 適したケース | 例 |
|---|---|---|
| threading | I/O待ちが多い処理 | Web API呼び出し、ファイルダウンロード |
| multiprocessing | CPU集約処理 | 画像処理、数値計算 |
| asyncio | 大量の同時I/O | Webサーバー、チャットアプリ |
まとめ
- GILにより、CPU集約処理はスレッドでは速くならない
- I/O待ち処理には
threadingまたはasyncioを使う - CPU集約処理には
multiprocessingを使う concurrent.futuresでスレッド/プロセスプールを簡潔に管理asyncioはasync/await構文で非同期I/Oを実現