Categories: python

Pythonで並列処理をマスターしよう!基礎から実践まで丁寧に解説

Pythonを使っていると、「もっと処理を速くしたい」「複数の作業を同時に実行したい」と思うことはありませんか?
そんなときに活用できるのが「並列処理」です。Pythonでは、threadingmultiprocessing などの標準モジュールを使うことで、比較的簡単に並列処理を取り入れることができます。この記事では、並列処理の基本概念から、代表的なモジュールの使い方、実践的なコード例までを初心者にもわかりやすく解説します。


並列処理とは?その基本を理解しよう

並列処理とは、複数の処理を同時に実行することを指します。これにより、時間のかかる処理を短縮し、プログラム全体のパフォーマンスを向上させることができます。

ただし、Pythonには「GIL(Global Interpreter Lock)」と呼ばれる制約があり、1つのインタプリタで複数スレッドが同時に実行できないという特徴があります。これが、Pythonの並列処理を少しややこしくしている理由です。


並列処理の種類:スレッドとプロセス

Pythonにおける並列処理は主に以下の2つに分けられます。

  • スレッド(threading):軽量でメモリ共有が可能。I/O処理に向いている。
  • プロセス(multiprocessing):独立したメモリ空間を持ち、CPUを活用する計算処理に向いている。

スレッドは処理を同時に見せかけることが得意ですが、CPU処理を多く行う場合はプロセスの方が効率的です。


threadingモジュールでマルチスレッドを実装する

Pythonの標準ライブラリであるthreadingを使えば、簡単にマルチスレッドを実装できます。以下はその基本例です。

import threading
import time

def task(name):
print(f"{name}開始")
time.sleep(2)
print(f"{name}終了")

thread1 = threading.Thread(target=task, args=("スレッド1",))
thread2 = threading.Thread(target=task, args=("スレッド2",))

thread1.start()
thread2.start()

thread1.join()
thread2.join()

print("すべてのスレッドが完了しました")

上記のように、Threadオブジェクトを生成し、start()メソッドで実行します。join()を使えば、すべてのスレッドが終了するまで待機できます。


multiprocessingモジュールでマルチプロセスを実装する

CPUをフル活用したいときはmultiprocessingモジュールが効果的です。以下に基本例を示します。

import multiprocessing
import time

def task(name):
print(f"{name}開始")
time.sleep(2)
print(f"{name}終了")

if __name__ == '__main__':
process1 = multiprocessing.Process(target=task, args=("プロセス1",))
process2 = multiprocessing.Process(target=task, args=("プロセス2",))

process1.start()
process2.start()

process1.join()
process2.join()

print("すべてのプロセスが完了しました")

マルチプロセスの場合、各プロセスは独立したメモリ空間を持つため、データの共有にはQueuePipeなどを使う必要があります。


並列処理で注意すべきポイント

並列処理には便利な点がある一方で、以下のような注意点もあります。

  • デバッグが難しくなる:並列実行中の不具合は再現性が低く、原因の特定が難しいことがあります。
  • リソースの競合:同じリソース(ファイルやデータなど)に複数のスレッド・プロセスがアクセスする場合、同期処理(ロック)が必要になります。
  • メモリ使用量:マルチプロセスではプロセスごとにメモリを使うため、大規模な並列実行はメモリ負荷に注意が必要です。

concurrent.futuresでより簡単に並列処理を

Python3.2以降では、concurrent.futuresモジュールを使うことで、スレッドもプロセスもシンプルに書けるようになりました。

from concurrent.futures import ThreadPoolExecutor
import time

def task(n):
print(f"タスク{n}開始")
time.sleep(2)
print(f"タスク{n}終了")

with ThreadPoolExecutor(max_workers=3) as executor:
for i in range(5):
executor.submit(task, i)

同様に、ProcessPoolExecutorを使えばプロセスベースの並列処理も行えます。

from concurrent.futures import ProcessPoolExecutor

def heavy_task(x):
return x * x

with ProcessPoolExecutor() as executor:
results = executor.map(heavy_task, range(10))
for r in results:
print(r)

並列処理を効果的に活用するために

並列処理は万能ではありませんが、以下のような場面では非常に効果を発揮します。

  • WebスクレイピングなどI/O待ちが多い処理
  • 大量の画像や動画を処理するバッチ処理
  • 並列で実行可能な機械学習の前処理

ただし、処理が依存し合うようなタスクでは並列化しても効果が出にくいため、状況に応じて適切な手法を選ぶことが重要です。


まとめ

Pythonでの並列処理は、処理の高速化において非常に強力な手段です。threadingmultiprocessing を使う基本から、concurrent.futuresでの簡易的な実装まで幅広く紹介しました。

あなたのプログラムにどのような並列処理が最適かを見極めて、ぜひ活用してみてください。正しく使えば、今まで何倍もの時間がかかっていた処理が、驚くほどスピードアップするはずです!

upandup

Web制作の記事を中心に、暮らし、ビジネスに役立つ情報を発信します。 アフィリエイトにも参加しています。よろしくお願いいたします。