You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Windows10下Python3.9使用ProcessPoolExecutor按两次Ctrl+C无限挂起问题

问题产生原因
  • 首先是Python 3.9版本在Windows平台对ProcessPoolExecutor的实现存在已知BUG:你使用with上下文管理器托管进程池时,上下文退出会自动调用shutdown(wait=True),你在异常捕获逻辑中又手动调用了一次shutdown,两次调用叠加导致关闭逻辑出现异常。
  • 第一次按下Ctrl+C触发KeyboardInterrupt进入异常处理逻辑后,第二次按下Ctrl+C刚好命中shutdown内部的管理线程join()调用,Windows平台下该调用的锁获取逻辑没有处理中断唤醒机制,会直接陷入永久阻塞。
  • Python 3.9版本的Windows平台中,shutdown传入的cancel_futures=True参数对已经启动的工作进程无效,无法主动终止正在运行的子进程,进一步加剧了挂起问题。
可行解决方案

方案1:升级Python版本

该BUG在Python 3.10及以上版本已经被官方修复,优化后的进程池中断处理逻辑支持正常响应两次Ctrl+C退出,cancel_futures参数也支持正常终止Windows平台已启动的工作进程,升级版本是成本最低的解决方案。

方案2:适配Python 3.9的代码修改

如果必须使用Python 3.9版本,调整代码逻辑避免重复调用shutdown,同时手动处理子进程终止即可解决问题:

  • 不要在with上下文内部手动调用executor.shutdown(),上下文退出时会自动执行关闭逻辑
  • 捕获KeyboardInterrupt后直接终止所有子进程,跳过默认的等待逻辑
    修改后参考代码如下:
import os
import signal
import concurrent.futures

# 你的业务逻辑
def Worker(x):
    pass

# 你的业务逻辑
def DoSomething(result):
    pass

if __name__ == "__main__":
    iterable = [] # 替换为你的迭代参数
    executor = concurrent.futures.ProcessPoolExecutor()
    try:
        results = executor.map(Worker, iterable, timeout=5)
        for result in results:
            DoSomething(result)
    except KeyboardInterrupt:
        # 手动杀死所有工作进程避免挂起
        for pid in executor._processes.keys():
            os.kill(pid, signal.SIGTERM)
        executor.shutdown(wait=False)
    except Exception as exc:
        print(exc)
        executor.shutdown(wait=True, cancel_futures=True)

方案3:替换进程池实现

如果上述方案都不适用,可以直接用multiprocessing.Pool替代ProcessPoolExecutor,前者在Windows平台的中断信号处理逻辑更成熟,相同场景下不会出现无限挂起问题。

内容的提问来源于stack exchange,提问作者Martin Bammer

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.07 01:00:03