記事一覧へ戻る 本の順番で続きを読む

並行処理 - マルチスレッドとマルチプロセス

Python3中級 | 2026/02/18 15:44

並行処理

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 でスレッド/プロセスプールを簡潔に管理
  • asyncioasync/await 構文で非同期I/Oを実現