1. 基礎編:Pythonのマルチプロセスとは?
1.1 マルチプロセスとは?
マルチプロセスとは、 複数のプロセス(独立した実行単位)を同時に動作させる技術 です。Pythonでは、multiprocessing
モジュールを使用して、簡単にマルチプロセスを実装できます。
マルチプロセスの特徴
- プロセスごとに独立したメモリ空間を持つ
- CPUコアを最大限活用できる
- プロセス間の通信が必要になる(
Queue
やPipe
を使用)
具体的な利用シーン
- 大量の計算を伴う処理(機械学習、数値シミュレーション)
- CPUをフル活用するタスク(画像処理、データ解析)
1.2 マルチスレッドとの違い
Pythonには「マルチスレッド」という並列処理の仕組みもあります。 マルチプロセスとマルチスレッドはどのように違うのでしょうか?
項目 | マルチプロセス | マルチスレッド |
---|---|---|
メモリ共有 | しない(独立したプロセス) | する(同じプロセス内) |
GILの影響 | 受けない | 受ける |
CPUバウンド向き | ◎ | ✕ |
I/Oバウンド向き | △ | ◎ |
データのやり取り | Queue やPipe が必要 | 共有メモリが使える |
GIL(Global Interpreter Lock)とは?
Pythonの標準インタプリタ(CPython)には「GIL」という仕組みがあり、 マルチスレッドを使っても同時に1つのスレッドしか実行できません。このため、 CPUをフル活用したい場合は、マルチプロセスが適しています。
1.3 Pythonでのマルチプロセスの簡単な実装例
import multiprocessing
import time
def worker(n):
print(f"プロセス {n} 開始")
time.sleep(2)
print(f"プロセス {n} 終了")
if __name__ == "__main__":
process_list = []
# 3つのプロセスを作成
for i in range(3):
p = multiprocessing.Process(target=worker, args=(i,))
process_list.append(p)
p.start()
# 全てのプロセスが終了するまで待機
for p in process_list:
p.join()
print("全プロセス終了")
1.4 マルチプロセスを使う際の注意点
1. Windowsでは if __name__ == "__main__":
が必須
Windows環境では、multiprocessing.Process()
を使う際に if __name__ == "__main__":
を書かないとエラーが発生 します。
間違ったコード(エラーになる)
import multiprocessing
def worker():
print("Hello from process")
p = multiprocessing.Process(target=worker)
p.start()
このコードは Windowsではエラーが発生 します。
正しいコード
import multiprocessing
def worker():
print("Hello from process")
if __name__ == "__main__":
p = multiprocessing.Process(target=worker)
p.start()
p.join()
if __name__ == "__main__":
を付けることで、Windowsでも正常に動作します。
1.5 まとめ
- マルチプロセスとは? → 複数のプロセスを並列に動かす手法
- マルチスレッドとの違い → GILの影響を受けず、CPUバウンドタスクに向いている
- Pythonでの簡単な実装例 →
multiprocessing.Process()
を使用 - Windowsでの注意点 →
if __name__ == "__main__":
が必要
2. 実践編:multiprocessingモジュールの使い方
2.1 multiprocessingモジュールの概要
multiprocessing
モジュールは、Pythonで プロセスベースの並列処理 を行うための標準ライブラリです。
このモジュールを使うことで、CPUコアをフル活用し、GILの制約を回避できます。
multiprocessingの主な機能
機能 | 説明 |
---|---|
Process | 個別のプロセスを作成し、実行する |
Queue | プロセス間でデータを送受信する |
Pipe | 2つのプロセス間でデータをやり取りする |
Value & Array | プロセス間で共有メモリを利用する |
Pool | プロセスのプールを作成し、効率的に並列処理を行う |
2.2 Process
クラスの基本的な使い方
Pythonで新しいプロセスを作成するには、multiprocessing.Process
クラスを使います。
基本的なプロセスの作成
import multiprocessing
import time
def worker(n):
print(f"プロセス {n} 開始")
time.sleep(2)
print(f"プロセス {n} 終了")
if __name__ == "__main__":
p1 = multiprocessing.Process(target=worker, args=(1,))
p2 = multiprocessing.Process(target=worker, args=(2,))
p1.start()
p2.start()
p1.join()
p2.join()
print("全プロセス終了")
2.3 プロセス間通信(Queue & Pipe)
Queueを使ったデータの送受信
import multiprocessing
def worker(q):
q.put("Hello from child process")
if __name__ == "__main__":
q = multiprocessing.Queue()
p = multiprocessing.Process(target=worker, args=(q,))
p.start()
p.join()
# 子プロセスからのデータを取得
print(q.get())
2.4 Value
と Array
を使った共有メモリ
import multiprocessing
def worker(val, arr):
val.value = 3.14 # 共有メモリの値を変更
arr[0] = 42 # 配列の値を変更
if __name__ == "__main__":
val = multiprocessing.Value('d', 0.0) # 'd' は double 型
arr = multiprocessing.Array('i', [0, 1, 2]) # 'i' は整数型
p = multiprocessing.Process(target=worker, args=(val, arr))
p.start()
p.join()
print(f"val: {val.value}, arr: {arr[:]}")
2.5 Pool
クラスを使ったプロセス管理
Pool
を使った並列処理
import multiprocessing
def square(n):
return n * n
if __name__ == "__main__":
with multiprocessing.Pool(4) as pool:
results = pool.map(square, range(10))
print(results)
2.6 まとめ
multiprocessing
モジュールを使うと 並列処理が簡単に実装できるProcess
クラスで個別のプロセスを作成Queue
やPipe
を使うと プロセス間でデータを共有可能Value
やArray
で メモリ共有Pool
クラスを使うと 大量のデータを効率的に処理

3. 応用編:エラーハンドリングとパフォーマンス最適化
3.1 multiprocessingで発生しやすいエラーと対策
エラー1:Windows環境での if __name__ == "__main__":
不在エラー
エラーメッセージ
RuntimeError: freeze_support() must be called if program is run in frozen mode
解決策
import multiprocessing
def worker():
print("Hello from process")
if __name__ == "__main__": # これが必要
p = multiprocessing.Process(target=worker)
p.start()
p.join()
エラー2:PicklingError(プロセス間で関数を渡せない)
エラーメッセージ
AttributeError: Can't pickle local object 'main.<locals>.<lambda>'
解決策
import multiprocessing
def square(x): # グローバル関数にする
return x * x
if __name__ == "__main__":
with multiprocessing.Pool(4) as pool:
results = pool.map(square, range(10)) # lambda を避ける
print(results)
エラー3:デッドロック(プロセスが停止したままになる)
解決策
import multiprocessing
def worker(q):
q.put("data")
if __name__ == "__main__":
q = multiprocessing.Queue()
p = multiprocessing.Process(target=worker, args=(q,))
p.start()
print(q.get()) # データを受け取る
p.join() # ここで正常に終了
3.2 パフォーマンス最適化のテクニック
最適化1:プロセス数を適切に設定する
import multiprocessing
def worker(n):
return n * n
if __name__ == "__main__":
num_workers = multiprocessing.cpu_count() # CPUコア数を取得
with multiprocessing.Pool(num_workers) as pool:
results = pool.map(worker, range(100))
print(results)
最適化2:Pool.starmap()
を使う
import multiprocessing
def multiply(a, b):
return a * b
if __name__ == "__main__":
with multiprocessing.Pool(4) as pool:
results = pool.starmap(multiply, [(1, 2), (3, 4), (5, 6)])
print(results)
最適化3:共有メモリを活用する
import multiprocessing
import ctypes
def worker(shared_array):
shared_array[0] = 99 # 共有メモリの値を変更
if __name__ == "__main__":
shared_array = multiprocessing.Array(ctypes.c_int, [1, 2, 3]) # 共有メモリを作成
p = multiprocessing.Process(target=worker, args=(shared_array,))
p.start()
p.join()
print(shared_array[:]) # [99, 2, 3]
3.3 まとめ
multiprocessing
で発生しやすい エラーの回避方法 を解説- パフォーマンス最適化 のポイント:
- プロセス数を適切に設定
- starmap() を活用
- 共有メモリで高速化
4. FAQ:よくある疑問と解決策
4.1 Pythonのマルチプロセスとマルチスレッド、どちらを使うべき?
回答
- CPUバウンド(計算負荷が高い処理) → マルチプロセス(multiprocessing)
- I/Oバウンド(ファイル・ネットワーク処理) → マルチスレッド(threading)
処理の種類 | 適した並列処理 |
---|---|
CPUバウンド(数値計算、画像処理など) | マルチプロセス(multiprocessing) |
I/Oバウンド(ファイル・APIリクエストなど) | マルチスレッド(threading) |
4.2 multiprocessing
が「遅い」と感じるのはなぜ?
回答
- プロセスの作成コストが高い →
Pool
を使う - データのコピーが多すぎる → 共有メモリ (
Value
,Array
) を使う - 小さなタスクを大量に処理 →
concurrent.futures.ThreadPoolExecutor
を試す
import multiprocessing
def worker(n):
return n * n
if __name__ == "__main__":
with multiprocessing.Pool(multiprocessing.cpu_count()) as pool:
results = pool.map(worker, range(100))
print(results)
4.3 マルチプロセスで辞書やリストを共有する方法は?
回答
multiprocessing.Manager()
を使う
import multiprocessing
def worker(shared_list):
shared_list.append(100) # 共有リストを更新
if __name__ == "__main__":
with multiprocessing.Manager() as manager:
shared_list = manager.list([1, 2, 3])
p = multiprocessing.Process(target=worker, args=(shared_list,))
p.start()
p.join()
print(shared_list) # [1, 2, 3, 100]
4.4 multiprocessing.Pool
で発生しやすいエラーとその対処法は?
エラー | 原因 | 解決策 |
---|---|---|
AttributeError: Can't pickle local object | lambda やローカル関数を渡している | グローバル関数を使う |
RuntimeError: freeze_support() must be called | Windows環境で if __name__ == "__main__": がない | if __name__ == "__main__": を追加 |
EOFError: Ran out of input | Pool でプロセスが正常に終了しなかった | pool.close() と pool.join() を適切に呼び出す |
4.5 Pythonのマルチプロセスをデバッグするには?
回答
multiprocessing.log_to_stderr()
を活用
import multiprocessing
import logging
def worker(n):
logger = multiprocessing.get_logger()
logger.info(f"プロセス {n} 実行中")
if __name__ == "__main__":
multiprocessing.log_to_stderr(logging.INFO) # ログを有効化
p = multiprocessing.Process(target=worker, args=(1,))
p.start()
p.join()
5. まとめと追加の学習リソース
5.1 この記事のまとめ
マルチプロセスの基本
- マルチプロセスとは? → 複数のプロセスを並列に動かし、CPUを最大限活用する技術
- マルチスレッドとの違い
- マルチプロセス → CPUバウンドな処理に適している(数値計算、画像処理など)
- マルチスレッド → I/Oバウンドな処理に適している(ファイル処理、ネットワーク通信など)
multiprocessing
モジュールの使い方
Process
クラスを使って 個別のプロセスを作成Queue
やPipe
を使って プロセス間でデータを送受信Value
やArray
を活用して 共有メモリを利用Pool
クラスで 効率的に並列処理を実行
エラーハンドリングとパフォーマンス最適化
- よくあるエラー
if __name__ == "__main__":
を書かないと Windowsでエラーlambda
やローカル関数は PicklingError が発生Queue.get()
を忘れると デッドロックの原因- パフォーマンス最適化
Pool
を活用してプロセスの作成コストを削減starmap()
を使って 複数の引数を渡すmultiprocessing.shared_memory
を利用して データコピーのオーバーヘッドを削減
5.2 追加の学習リソース
1. Python公式ドキュメント
2. オンラインチュートリアル
5.3 これからの活用に向けて
Pythonの multiprocessing
を適切に活用することで、CPUを効率的に使用し、 パフォーマンスの高いプログラムを作成できます。
次に学ぶべき技術
- 非同期処理(asyncio) → I/Oバウンドな処理を並列化
- concurrent.futures → スレッドとプロセスの統合管理
5.4 おわりに
本記事では 「Python マルチプロセス」 について 基礎から実践、応用まで詳しく解説 しました。
この記事で学んだ知識を活かし、実際のプロジェクトに適用 してみてください! 🚀