はじめに
プログラミングにおける並行処理は、複数のタスクを同時に実行する方法です。Pythonでは、スレッド、プロセス、非同期I/Oなどの手法を利用して並行処理を実現できます。
この記事では、Pythonで利用できる並行処理の基礎、各手法の違い、そして適切な使い分けについて詳しく解説します。
並行処理とは
並行処理と並列処理の違い
- 並行処理(Concurrency):
複数のタスクを切り替えながら進行させること。CPUコアが1つでも実現可能。 - 並列処理(Parallelism):
複数のタスクを同時に実行すること。複数のCPUコアが必要。
Pythonでは、これらを区別しながら適切な処理方法を選択することが重要です。
Pythonの並行処理の主な手法
- スレッド(
threading
モジュール)
軽量なスレッドを使って、同一プロセス内で複数のタスクを実行します。 - プロセス(
multiprocessing
モジュール)
各プロセスが独立して動作し、並列処理に適しています。 - 非同期I/O(
asyncio
モジュール)
非同期イベントループを利用して、I/Oバウンドのタスクを効率化します。
スレッドを使った並行処理
スレッドの概要
スレッドは、1つのプロセス内で独立した処理を実行する軽量な単位です。同じメモリ空間を共有するため、データのやり取りが高速ですが、競合状態(Race Condition)に注意が必要です。
スレッドの使い方
例:スレッドの基本
import threading
def print_numbers():
for i in range(5):
print(i)
thread = threading.Thread(target=print_numbers)
thread.start()
thread.join()
説明:
threading.Thread
: 新しいスレッドを作成します。start()
: スレッドを開始します。join()
: スレッドの終了を待機します。
スレッドの課題
- グローバルインタプリタロック(GIL)
PythonのスレッドはGILにより制限され、CPUバウンドの処理ではパフォーマンスが向上しにくい。 - スレッド間のデータ競合
メモリを共有するため、データ競合を防ぐための同期が必要。
プロセスを使った並列処理
プロセスの概要
プロセスは、各プロセスが独立したメモリ空間を持ち、完全に並列で動作します。GILの影響を受けないため、CPUバウンドのタスクに適しています。
プロセスの使い方
例:プロセスの基本
from multiprocessing import Process
def print_numbers():
for i in range(5):
print(i)
process = Process(target=print_numbers)
process.start()
process.join()
説明:
multiprocessing.Process
: 新しいプロセスを作成します。start()
: プロセスを開始します。join()
: プロセスの終了を待機します。
プロセスの課題
- メモリ使用量
プロセスごとに独立したメモリ空間を持つため、メモリ消費が増加します。 - データ共有の複雑さ
プロセス間でデータを共有する場合、Queue
やPipe
などの仕組みを利用する必要があります。
非同期I/Oを使った処理
非同期I/Oの概要
非同期I/Oは、I/O操作を効率化するための手法です。イベントループを活用し、待機中の時間を他のタスクに割り当てることで効率的な処理を実現します。
非同期I/Oの使い方
例:非同期関数の基本
import asyncio
async def print_numbers():
for i in range(5):
print(i)
await asyncio.sleep(1)
asyncio.run(print_numbers())
説明:
async def
: 非同期関数を定義します。await
: 非同期タスクを待機します。asyncio.run
: イベントループを実行します。
非同期I/Oの利点
- I/Oバウンドタスクの効率化
ネットワーク通信やファイル操作が多いタスクに最適。 - シンプルなコード
イベント駆動型のコードを直感的に記述可能。
各手法の比較と選択
特徴 | スレッド | プロセス | 非同期I/O |
---|---|---|---|
並列性 | 制限あり(GILの影響) | 完全な並列 | 並列性はないが効率的 |
用途 | 軽量タスクの実行 | CPUバウンドの処理 | I/Oバウンドの処理 |
メモリ使用量 | 低い | 高い | 非常に低い |
データ共有 | 簡単(共有メモリ) | 複雑(Queue/Pipe) | 簡単(同じイベントループ) |
まとめ
Pythonの並行処理は、タスクの種類に応じてスレッド、プロセス、非同期I/Oを適切に選択することで、効率的なプログラムを構築できます。この記事で解説した基本と応用を参考に、プロジェクトに最適な並行処理の方法を実装してみてください!
コメント