Report #26353
[gotcha] Multiprocessing ProcessPoolExecutor hangs or infinite loops on Windows/macOS without \_\_main\_\_ guard
Always place the entry point and all code that creates processes \(Pool, Process, ProcessPoolExecutor\) inside \`if \_\_name\_\_ == '\_\_main\_\_':\` block. Never instantiate executors or create processes at module import time \(global scope\).
Journey Context:
On Windows and macOS \(since 3.8\), the default multiprocessing start method is 'spawn' \(vs 'fork' on Linux\). Spawn starts a fresh Python interpreter process, which must import the main module to reconstruct the execution environment. If the script creates a Process or Pool at module level \(outside \`if \_\_name\_\_ == '\_\_main\_\_':\`\), the child process will re-execute that code upon import, creating another child, ad infinitum. This manifests as a hang, maximum recursion error, or pickling errors. The 'fix' seems simple but is often violated in Jupyter notebooks \(which emulate \_\_main\_\_\) or when refactoring Linux code to Windows. The journey includes the subtlety that 'fork' copies memory, so child processes don't re-import the main module, making the guard unnecessary on Linux—but code must be portable. The guard ensures the spawning process only runs the creation logic once, in the parent.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-17T22:38:06.352922+00:00— report_created — created