Python 3.14でProcessPoolExecutorが失敗するときの直し方:forkserver・pickle・mainガードを確認

結論:Python 3.14ではforkはどのOSでも既定ではありません。Linuxなどforkserver対応POSIXでは既定がforkserverへ変わり、WindowsとmacOSはspawnです。従来のforkで動いていたコードは、親の状態を引き継ぐ前提、mainの副作用、pickleできない関数やデータが表面化します。まず実際の起動方式を確認し、ワーカーをimport可能な形へ直します。

ProcessPoolExecutorの環境確認から修正と再確認までの流れ
失敗原因の切り分け順
目次

1. Python・OS・起動方式を確認する

次をプロジェクト直下のcheck_start_method.pyとして保存し、問題が出る環境のPythonで実行します。

import sys
import multiprocessing as mp

print(sys.version)
print(sys.platform)
print(mp.get_all_start_methods())
print(mp.get_start_method())

プロジェクト直下で、対象のPython環境を使って実行します。

python check_start_method.py

get_all_start_methods()の先頭は環境の既定です。ただし、ProcessPoolExecutorにmp_contextを渡せばその指定が優先されます。さらに、max_tasks_per_childを指定してmp_contextを省略するとspawnが使われます。「Pythonの既定がforkserverだから、このExecutorもforkserver」とは限りません。生成箇所の両引数も確認してください。

環境Python 3.14の基本的な既定注意
Linux等の対応POSIXforkserver実際の一覧で対応確認
macOSspawnforkは非推奨
Windowsspawnforkserver・forkは非対応

2. BrokenProcessPoolだけで原因を決めない

BrokenProcessPoolはワーカーが正常でない形で終了した結果で、原因名ではありません。直前の子プロセス側トレースバックや標準エラーまで読み、次を分けます。

  • 子がmainをimportした際、トップレベルで再びプロセスを作っていないか
  • callable、引数、返り値がpickle可能か。lambda、ローカル関数、REPL関数ではないか
  • 親で変更したglobalを子も同じ値で見る前提になっていないか
  • initializer、依存のimport、ネイティブ拡張、OOMやシグナル終了ではないか

次は、spawnやforkserverで問題になるトップレベル実行の例です。

from concurrent.futures import ProcessPoolExecutor

def square(number):
    return number * number

with ProcessPoolExecutor(max_workers=2) as executor:
    print(list(executor.map(square, [1, 2, 3])))

子がファイルをimportするとExecutor作成まで再実行されます。プロセス作成はmainガード内へ移し、ワーカーはlambdaやmain内のローカル関数ではなく、モジュールのトップレベルへ置きます。

3. mainガードとcontextを明示して直す

次をプロジェクト直下のprocesspool_fixed.pyとして保存します。指定方式が利用可能かを確認し、そのcontextをExecutorへ渡します。

import argparse
import multiprocessing as mp
from concurrent.futures import ProcessPoolExecutor

def square(number):
    return number * number

def main():
    parser = argparse.ArgumentParser()
    parser.add_argument('--method', default=None)
    args = parser.parse_args()

    if args.method and args.method not in mp.get_all_start_methods():
        raise SystemExit('unsupported start method: ' + args.method)

    context = mp.get_context(args.method)
    print('method:', context.get_start_method())

    with ProcessPoolExecutor(max_workers=2, mp_context=context) as executor:
        print(list(executor.map(square, [1, 2, 3])))

if __name__ == '__main__':
    main()

Windows・macOS・Linuxで、まず既定contextを確認します。

python processpool_fixed.py

次はforkserver対応Linux等のPOSIX環境で、起動方式の一覧にforkserverがある場合だけ実行します。Windows・macOSでの既定方式確認には使いません。

python processpool_fixed.py --method forkserver

期待値は環境依存です。Windows/Python 3.12.14では--method spawnでmethod: spawnと[1, 4, 9]を実測し、forkserverは非対応として停止することを確認しています。Linux/Python 3.14のforkserver実行は本稿では未実測で、挙動の説明は公式資料に基づきます。REPLやNotebookの一時定義と、ファイル実行を混同しないでください。

4. globalとimport時の副作用を整理する

spawnとforkserverでは、子が見るglobalが親のProcess.start()時点と同じとは限りません。必要な設定や処理対象は引数またはinitializerの引数で渡します。import時にファイル操作、接続、Executor作成を行わず、実行処理はmainガード内へ置きます。

5. forkへ戻すのは限定措置

対応POSIXで旧コードを一時的に動かす場合は、利用可能なことを確認して明示します。

次は上の例のimportとsquareを使い、main()内のcontext作成・Executor部分を置き換える断片です。main()内に合わせてインデントし、mainガードを保持してください。この断片だけでは単独実行できません。

context = mp.get_context('fork')
with ProcessPoolExecutor(mp_context=context) as executor:
    print(list(executor.map(square, [1, 2, 3])))

forkはマルチスレッドとの組み合わせに問題があり、Python 3.12以降は検出時に警告対象です。Windowsでは使えず、macOSでも推奨されません。移行の第一選択ではなく、限定的な互換措置にしてください。max_tasks_per_childはforkと非互換です。

6. mainとpickleを直してもhangする場合

Pythonのパッチ版も確認します。公式3.14.8ドキュメントでは、3.14.7で、ワーカーがmax_tasks_per_child上限に達して終了し、待機タスクが残るとExecutorが停止する不具合を修正したとされています。該当条件なら3.14.7以降へ更新します。ほかにinitializer例外、ネイティブクラッシュ、OOM、子からExecutorやFutureのメソッドを呼ぶデッドロックも確認してください。

FAQ

Q. Python 3.14のLinuxなら必ずforkserverですか?
対応POSIXでは既定ですが、mp_contextやmax_tasks_per_childで変わります。実測値と生成コードを確認します。

Q. mainガードを付ければlambdaも使えますか?
いいえ。mainガードとpickle要件は別です。lambda、ローカル関数、REPL関数は期待どおり動くと保証されません。

Q. BrokenProcessPoolはpickleエラーですか?
断定できません。initializer失敗、import例外、ネイティブクラッシュ、外部終了などでも発生します。

一次資料

確認日:2026年10月5日。

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次