Python将 futures与分叉块结合起来,永远存在

编程语言 2026-07-07

下面是一个最小可复现的示例

import os
import concurrent.futures
import multiprocessing

tm = concurrent.futures.ProcessPoolExecutor(1, multiprocessing.get_context("spawn"))


def foo() -> None:

    pipe_r, pipe_w = os.pipe()
    if c := os.fork():
        os.close(pipe_r)
        return

    os.close(pipe_w)
    os.setsid()
    try:
        os.read(pipe_r, 1)
    finally:
        os._exit(0)


foo()

运行时,这个会一直阻塞。如果我移除ProcessPoolExecutor,它可以工作。此外它在3.12.13版会失败,但在3.8版则可工作。显然发生了死锁,但我还在努力理解为什么?

解决方案

问题在于forking一个多线程进程并不安全:锁(以及其他线程状态)可能在拷贝到子进程时处于锁定状态,子进程会一直等待那些在子进程中不再存在的线程。这在你从一个有线程/工作器机制活跃的进程调用 os.fork() 时,死锁就有可能发生(例如并发池、日志线程、库创建的后台线程等)。

“父进程使用os.fork() 来分叉Python解释器。子进程在开始时,实质上与父进程相同。父进程的所有资源都被子进程继承。请注意,对多线程进程安全地fork是有问题的。”

https://docs.python.org/3/library/multiprocessing.html#contexts-and-start-methods

这解释了为什么移除 ProcessPoolExecutor(它创建了线程/后台状态和工作进程)会让问题消失。在那套机制还存在时进行fork可能会拷贝一个后台线程持有的锁;子进程因此在该锁上永久阻塞。

你可以参考这个。看起来似乎与你遇到的问题类似:
Python multiprocessing pool some process in deadlock when forked but runs when spawned

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章