WindowsでのPython Multiprocessing 'RuntimeError'の修正方法

beginner🐍 Python2026-07-10| Windows OS, Python 3.x, Multiprocessingライブラリ

Error Message

RuntimeError: An attempt has been made to start a new process before the current process has finished its bootstrapping phase
#python#multiprocessing#windows#デバッグ

エラーについて

Windowsでmultiprocessingモジュールを使用してPythonスクリプトを高速化しようとした際、イライラするような壁にぶつかったことがあるかもしれません。コードが速くなるどころか、RuntimeErrorで終わる大量のテキストが表示されます。

RuntimeError: An attempt has been made to start a new process before the current process has finished its bootstrapping phase.

This probably means that you are not using fork to start your child processes and you have forgotten to use the proper idiom in the main module:

    if __name__ == '__main__':
        freeze_support()
        ...

なぜこれが発生するのか

このクラッシュは、OSが新しいプロセスを処理する方法の根本的な違いに起因しています。LinuxやmacOSは通常、fork()を使用します。これにより、親プロセスの同一のクローンである子プロセスが作成され、メモリ内のすべてを継承します。

Windowsにはfork()がありません。代わりに、spawnと呼ばれるメソッドを使用します。Windowsで子プロセスを開始すると、Pythonは完全に新しいインタープリタを起動します。この新しいインスタンスに関数や変数を読み込むために、Pythonはスクリプトを最初から再インポートする必要があります。

ここで論理ループが発生します。サブプロセスを開始するコードがスクリプトの「トップレベル」にある場合、子プロセスがファイルをインポートする際にその同じ行を実行してしまいます。これにより2番目の子プロセスがトリガーされます。その子が3番目をトリガーし、といった具合です。この再帰的な連鎖反応は、CPU使用率が急上昇し、システム全体のフリーズを防ぐためにPythonがスクリプトを強制終了するまで続きます。

解決策:Mainガード

修正は単純な構造変更です。新しいプロセスを開始するコードはすべて、if __name__ == '__main__':ブロック内にラップする必要があります。この条件チェックにより、コードはスクリプトを直接実行したときにのみ実行され、子プロセスがインポートしたときには実行されないようになります。

誤ったコード(ループを引き起こす例)

import multiprocessing
import time

def worker():
    print("ワーカーが開始されました")
    time.sleep(2)

# この行はインポート時に即座に実行され、Windowsで再帰を引き起こします
p = multiprocessing.Process(target=worker)
p.start()
p.join()

正しいコード(修正案)

import multiprocessing
import time

def worker():
    print("ワーカーが開始されました")
    time.sleep(2)

if __name__ == '__main__':
    # このブロックは保護されており、メインプロセスでのみ実行されます
    p = multiprocessing.Process(target=worker)
    p.start()
    p.join()
    print("ワーカーが終了しました")

PoolやQueueの扱い

同じロジックがmultiprocessing.Poolにも適用されます。もしmainガードの外で4つのワーカーのプールを定義すると、Windowsは4つの新しいプロセスを生成しようとし、それぞれがさらに4つを生成しようとします。その結果、スクリプトはほぼ瞬時にクラッシュします。

import multiprocessing

def square(n):
    return n * n

if __name__ == '__main__':
    # プロセス数を明示的に定義する方が安全な場合が多いです
    with multiprocessing.Pool(processes=4) as pool:
        results = pool.map(square, [1, 2, 3, 4, 5])
        print(f"結果: {results}")

修正を確認する方法

ガードを適用したら、次の3つの方法でスクリプトをテストしてください。

- **ターミナルでの実行:** PowerShellで`python your_script.py`を実行します。ブートストラップエラーが発生することなく、最後まで実行されるはずです。
- **タスクマネージャーの監視:** スクリプトの実行中にタスクマネージャー(Ctrl+Shift+Esc)を開きます。`processes=4`に設定した場合、1つの親プロセスと4つのワーカーの合計5つの`python.exe`インスタンスが表示されるはずです。
- **ゴーストプロセスの確認:** スクリプトが終了したら、すべてのPythonプロセスが消えることを確認してください。もし残っている場合は、`join()`や同期の問題がある可能性があります。

Windowsユーザーのためのベストプラクティス

並列コードを安定させるために、以下の習慣を推奨します。

- **常にガードを使用する:** すべてのスクリプトで`if __name__ == '__main__':`を記述することをデフォルトの習慣にしましょう。これにより、将来的に関数を別のプロジェクトにインポートすることになった場合でも、予期しないサイドエフェクトを防ぐことができます。
- **トップレベルをクリーンに保つ:** ファイルのトップレベルには、定数、クラス、関数の定義のみを記述してください。プリント出力やプールの開始などの「アクション」は、すべて関数内またはmainガード内に記述します。
- **freeze_support()を追加する:** PyInstallerを使用してスクリプトをスタンドアロンの`.exe`にする予定がある場合は、mainガード内の最初の行として`multiprocessing.freeze_support()`を呼び出してください。

Related Error Notes