Pythonのmultiprocessingモジュール完全ガイド【並列処理でパフォーマンス最大化】
Pythonのmultiprocessingモジュール:並列処理の徹底ガイド【実例付き】
Python multiprocessing を使うと、マルチコアCPUを活かしてプログラムを高速化できます。
本記事では、基本的な使い方から、戻り値の扱い、for文でのプロセス起動、共有メモリ(Value / Array)、Pipeによる通信、Windowsでの注意点、threadingとの違いまでをまとめて解説します。
multiprocessingモジュールの基本【Python multiprocessing 使い方の全体像】
Pythonのmultiprocessingモジュールは、「プロセス単位」で並列処理を行うための標準ライブラリです。
threadingと違い、GIL(Global Interpreter Lock)の制約を受けず、CPUバウンドな処理の高速化に向いています。
- GILの制約を回避し、真の並列処理を実現
- プロセス間でデータを共有する手段(共有メモリ、Queue、Pipe、Managerなど)を提供
- CPUバウンドのタスク(大量計算、画像処理、機械学習前処理など)に最適
「Python multiprocessing 使い方」を理解するために、まずは最低限の構成から見ていきます。
Python multiprocessingの基本的な使い方
例1: 最もシンプルなプロセス生成
from multiprocessing import Process
def worker():
print("ワーカーが実行されています!")
if __name__ == "__main__":
p = Process(target=worker)
p.start() # プロセス開始
p.join() # 終了待ち
この例が Python multiprocessing 使い方 の最小構成です。
Windows環境では特に、必ず if __name__ == "__main__": の中でプロセスを生成する必要があります(後述)。
例2: Python multiprocessing for文で複数プロセスを起動
複数のプロセスを起動したい場合、「Python multiprocessing for文」で繰り返し起動するのが定番です。
from multiprocessing import Process
import time
def worker(num):
print(f"プロセス {num} 開始")
time.sleep(1)
print(f"プロセス {num} 終了")
if __name__ == "__main__":
processes = []
for i in range(5): # for文で複数プロセスを生成
p = Process(target=worker, args=(i,))
processes.append(p)
p.start()
for p in processes:
p.join()
このように、forループでプロセスをまとめて起動し、後半のループでjoin()するのが典型パターンです。
例3: プロセスプールで戻り値をまとめて取得(Pool.map)
「Python multiprocessing 戻り値」を簡単に扱うなら、Poolを使った方法が最もわかりやすいです。
from multiprocessing import Pool
def square(n):
return n * n
if __name__ == "__main__":
with Pool(4) as p:
# Python multiprocessing 戻り値 をリストで受け取る
results = p.map(square, [1, 2, 3, 4, 5])
print(results) # [1, 4, 9, 16, 25]
Pool.mapは、「引数リスト」を受け取り、「戻り値のリスト」を順番通りに返してくれるため、戻り値を扱う定番パターンです。
Python multiprocessing 戻り値の受け取り方まとめ
Python multiprocessing 戻り値 を扱う方法はいくつかあります。代表的なのは次の3つです。
Pool.map/Pool.starmapでリストとして受け取るapply_asyncで非同期に受け取り、get()で取得QueueやManager.listなど共有オブジェクトに書き込む
1. Pool.starmapで複数引数+戻り値
from multiprocessing import Pool
def add(a, b):
return a + b
if __name__ == "__main__":
args = [(1, 2), (3, 4), (5, 6)]
with Pool(3) as p:
results = p.starmap(add, args)
print(results) # [3, 7, 11]
2. apply_asyncを使った非同期な戻り値取得
from multiprocessing import Pool
import time
def compute(x):
time.sleep(1)
return x * x
if __name__ == "__main__":
with Pool(4) as p:
async_result = p.apply_async(compute, (10,))
# 他の処理をここで行える
result = async_result.get() # 戻り値を取得
print(result) # 100
3. Queueを使って戻り値を集める
from multiprocessing import Process, Queue
def worker(x, q):
q.put(x * x) # 戻り値をQueueへ
if __name__ == "__main__":
q = Queue()
processes = [Process(target=worker, args=(i, q)) for i in range(5)]
for p in processes:
p.start()
for p in processes:
p.join()
results = [q.get() for _ in range(5)]
print(results)
複数プロセスからの結果を逐次集めたいときは、Queueも便利です。
Python multiprocessing 共有メモリの基本
プロセスは通常、メモリ空間を共有しませんが、Python multiprocessing 共有メモリとして
ValueやArrayを使うことで、特定の値を共有できます。
Python multiprocessing Valueで単一値を共有
from multiprocessing import Value, Process
def increment(shared_value):
for _ in range(1000):
shared_value.value += 1
if __name__ == "__main__":
# 'i' は C のint型(整数)を表す typecode
counter = Value('i', 0) # 初期値0
processes = [Process(target=increment, args=(counter,)) for _ in range(4)]
for p in processes:
p.start()
for p in processes:
p.join()
print("最終カウント:", counter.value)
このように Python multiprocessing Value を使うと、複数プロセス間で1つの整数値などを共有できます。
実際には競合を防ぐためにLockを組み合わせることも多いです。
Arrayで配列を共有する
from multiprocessing import Process, Array
def worker(shared_arr):
for i in range(len(shared_arr)):
shared_arr[i] *= 2
if __name__ == "__main__":
arr = Array('i', [1, 2, 3, 4])
p = Process(target=worker, args=(arr,))
p.start()
p.join()
print(list(arr)) # [2, 4, 6, 8]
Managerを使った高レベルな共有オブジェクト
from multiprocessing import Process, Manager
def worker(shared_list, value):
shared_list.append(value)
if __name__ == "__main__":
with Manager() as manager:
shared_list = manager.list()
processes = [Process(target=worker, args=(shared_list, i)) for i in range(5)]
for p in processes:
p.start()
for p in processes:
p.join()
print(list(shared_list)) # [0,1,2,3,4](順序は保証されない場合あり)
Managerはリストや辞書といった高レベルオブジェクトを共有したい場合に便利です。
Python multiprocessing PipeとQueueでのプロセス間通信
プロセス間でデータをやり取りするには、QueueだけでなくPython multiprocessing Pipeもよく使われます。
Python multiprocessing Pipeの基本例
from multiprocessing import Process, Pipe
def sender(conn):
conn.send("子プロセスからのメッセージ")
conn.close()
if __name__ == "__main__":
parent_conn, child_conn = Pipe()
p = Process(target=sender, args=(child_conn,))
p.start()
msg = parent_conn.recv() # Pipeから受信
print(msg)
p.join()
Pipe()は2つの接続端点(parent_conn / child_conn)を返し、一方向・双方向の通信に利用できます。
Queueとの使い分け
- 複数プロセスが同じキューにデータを流したい → Queueが向いている
- 1対1の通信でシンプルにやり取りしたい → Pipeがシンプル
multiprocessingの実務的な活用例
例1: 大量データを並列処理
from multiprocessing import Pool
import time
def compute(x):
time.sleep(1) # 重い計算の代わり
return x * x
if __name__ == "__main__":
with Pool(4) as p:
results = p.map(compute, range(10))
print(results)
例2: キューを利用したプロセス間通信
from multiprocessing import Process, Queue
def worker(q):
q.put("データ送信")
if __name__ == "__main__":
q = Queue()
p = Process(target=worker, args=(q,))
p.start()
print(q.get())
p.join()
例3: 並列Webスクレイピング
from multiprocessing import Pool
import requests
def fetch_url(url):
response = requests.get(url)
return url, response.status_code
if __name__ == "__main__":
urls = ["https://example.com" for _ in range(5)]
with Pool(5) as p:
results = p.map(fetch_url, urls)
print(results)
例4: 画像処理の並列化
from multiprocessing import Pool
from PIL import Image
def process_image(image_path):
img = Image.open(image_path)
img = img.convert("L")
img.save("processed_" + image_path)
if __name__ == "__main__":
image_files = ["image1.jpg", "image2.jpg", "image3.jpg"]
with Pool(3) as p:
p.map(process_image, image_files)
例5: データベースクエリの並列実行
from multiprocessing import Pool
import sqlite3
def fetch_data(query):
conn = sqlite3.connect("example.db")
cursor = conn.cursor()
cursor.execute(query)
result = cursor.fetchall()
conn.close()
return result
if __name__ == "__main__":
queries = ["SELECT * FROM users", "SELECT * FROM orders"]
with Pool(2) as p:
results = p.map(fetch_data, queries)
print(results)
Python multiprocessing Windows環境での注意点
「Python multiprocessing Windows」でよくつまずくポイントは、プロセスの生成方法です。Windowsではプロセス生成方式が spawn のため、
if __name__ == "__main__": ブロックの外でプロセスを生成すると無限ループのような挙動になることがあります。
- 必ず
if __name__ == "__main__":の中でプロセス・Poolを生成する - PyInstallerなどでEXE化する場合は
freeze_support()が必要になるケースがある - Jupyter環境ではうまく動かないことがあるので、
.pyスクリプトで実行するのが安全
from multiprocessing import Process, freeze_support
def worker():
print("Windowsでも安全に実行")
if __name__ == "__main__":
freeze_support() # EXE化時などに必要になる場合がある
p = Process(target=worker)
p.start()
p.join()
Python multiprocessing threadingとの違いと使い分け
「Python multiprocessing threading どっちを使うべきか?」はよくある疑問です。
multiprocessingが向いているケース
- CPUバウンドな処理(数値計算、画像処理、機械学習前処理など)
- GILの影響を避けて高速化したいとき
threadingが向いているケース
- I/Oバウンドな処理(ネットワーク通信、ファイル読み書き、API呼び出し)
- 軽量でシンプルに並行処理したいとき
ざっくり言うと、CPUをガンガン使うならmultiprocessing、I/O待ちが多いならthreadingというイメージで選ぶと失敗しにくくなります。
次に読むべき記事
まとめ:Python multiprocessingでCPUをフル活用しよう
本記事では、Python multiprocessing 使い方を軸に、for文でのプロセス起動、戻り値の受け取り方、 共有メモリ(Value・Array・Manager)、Pipe/Queueによる通信、Windows環境での注意点、 そしてthreadingとの違いまでを解説しました。
まずはこの記事のサンプルコードを手元で動かしてみて、「単一プロセスのコードを、multiprocessingに書き換えるとどう変わるか?」を体感してみるのがおすすめです。
さらにPython全体を体系的に学びたい方はこちら:
Python完全ガイドを見る