Python における並列処理の完全ガイド

Programming

はじめに

Python は使いやすく強力なプログラミング言語ですが、並列処理を実装する際には GIL(Global Interpreter Lock) という制約があり、適切な手法を選択することが重要です。本記事では、Python における並列処理の主要な手法である スレッド(threading)、プロセス(multiprocessing)、非同期処理(asyncio) について詳しく解説します。

本記事を読むことで、Python における並列処理の仕組みを理解し、適切な手法を選択できるようになります。


1. Python における並列処理の基本概念

並列処理を理解するためには、以下の2つの概念を区別することが重要です。

  • 並行処理(Concurrency):複数のタスクを「同時に進める」ように見せる技術。実際には 1 つの CPU コアがタスクを切り替えて処理している。
  • 並列処理(Parallelism):複数のタスクを「完全に同時に実行する」技術。複数の CPU コアを使用して同時に処理を行う。

Python には、並行処理と並列処理を実現するためのいくつかの手法があります。

手法並行処理 or 並列処理CPUバウンドI/Oバウンド
threading並行処理❌ 適さない✅ 適している
multiprocessing並列処理✅ 最適❌ 適さない
asyncio並行処理❌ 不適✅ 最適

2. threading モジュール(スレッド並列処理)

特徴

  • Python の threading モジュールを使用すると、複数のスレッドを作成して並行に実行できる。
  • ただし GIL により 1 つのスレッドしか同時に動作できない
  • I/O バウンド(ファイル操作、ネットワーク通信など)に適している。

実装例(ファイルダウンロードの並列化)

import threading
import time

def download_file(url):
    print(f"{url} のダウンロード開始")
    time.sleep(2)
    print(f"{url} のダウンロード完了")

urls = ["https://example.com/file1", "https://example.com/file2"]

threads = []
for url in urls:
    t = threading.Thread(target=download_file, args=(url,))
    t.start()
    threads.append(t)

for t in threads:
    t.join()

print("すべてのダウンロード完了")

メリットとデメリット

✅ I/O バウンドの処理に適している

multiprocessing よりもメモリ消費が少ない

❌ GIL の影響を受けるため、CPU バウンドの処理には向かない

❌ スレッド間のデータ競合やデッドロックのリスクがある


3. multiprocessing モジュール(プロセス並列処理)

特徴

  • multiprocessingプロセス を作成し、GIL の制約を受けずに並列処理を実現。
  • CPU バウンド(計算負荷が高い処理)に最適。
  • プロセス間でメモリを共有しないため、データの受け渡しには QueueManager が必要。

実装例(CPU負荷の高い計算)

import multiprocessing
import time

def heavy_computation(n):
    print(f"計算開始: {n}")
    result = sum(i * i for i in range(n))  # 重い計算
    print(f"計算完了: {n}, 結果: {result}")

if __name__ == "__main__":
    numbers = [10**7, 10**7]

    processes = []
    for n in numbers:
        p = multiprocessing.Process(target=heavy_computation, args=(n,))
        p.start()
        processes.append(p)

    for p in processes:
        p.join()

    print("すべての計算が完了しました")

メリットとデメリット

✅ GIL の影響を受けないため CPU をフル活用できる

✅ 機械学習、データ分析、画像処理などに最適

❌ プロセス作成のオーバーヘッドが大きい

❌ メモリ消費が多く、大量のプロセスを作るとメモリを圧迫


4. asyncio モジュール(非同期処理)

特徴

  • asyncio はイベントループを使用し、スレッドやプロセスを作成せずに 非同期処理 を実現。
  • ネットワーク通信、ファイル操作、データベースアクセスなどの I/O バウンド処理 に最適。

実装例(非同期のAPIリクエスト)

import asyncio

async def fetch_data(url):
    print(f"{url} のデータ取得開始")
    await asyncio.sleep(2)
    print(f"{url} のデータ取得完了")

async def main():
    urls = ["https://example.com/data1", "https://example.com/data2"]
    await asyncio.gather(*(fetch_data(url) for url in urls))

asyncio.run(main())

メリットとデメリット

✅ スレッドやプロセスを作らず軽量で効率的

✅ ネットワーク通信やファイル操作などに最適

❌ CPU バウンドの処理には向かない

async に対応していないライブラリとの互換性が低い


5. まとめ

手法並行処理 or 並列処理CPUバウンドI/Oバウンド
threading並行処理
multiprocessing並列処理
asyncio並行処理

どの手法を選ぶべきか?

  • CPU負荷の高い処理(機械学習、データ分析)multiprocessing
  • ネットワーク通信、データベース処理asyncio
  • ファイル操作、I/O 待ちが多い処理threading

Python で最適な並列処理を選び、パフォーマンスを最大化しましょう!

タイトルとURLをコピーしました