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

Python multiprocessing.Pool中Ctrl+C触发KeyboardInterrupt的处理问题

问题原因

当你在终端按下Ctrl+C时,内核会向整个前台进程组发送SIGINT信号,而非仅发送给主进程。Python默认对SIGINT的处理逻辑是抛出KeyboardInterrupt异常,而multiprocessing使用fork方式创建工作进程时,子进程会完全继承父进程的信号处理规则,因此主进程和子进程会同时收到信号、同时触发异常,这就是你看到子进程打印traceback的根本原因。

你原有的预期存在偏差:with mp.Pool()的上下文清理逻辑,需要等主进程从当前阻塞的starmap调用中退出后才会执行,但子进程收到SIGINT后会立刻触发异常,根本等不到主进程发起的终止流程,自然不会出现你以为的「子进程收到SIGTERM静默退出」的效果。

实现优雅退出的方案

核心思路是把SIGINT的处理权限完全收归主进程:工作进程启动后第一时间忽略SIGINT信号,只有主进程响应Ctrl+C,再通过Pool自带的清理逻辑正常终止所有子进程即可。

修正后的可运行代码如下:

import os
import multiprocessing as mp
import signal


def _init_worker():
    # 工作进程忽略SIGINT信号,所有退出逻辑由主进程统一调度
    signal.signal(signal.SIGINT, signal.SIG_IGN)


def g():
    print(os.getpid())
    while True:
        pass
        
        
def main():
    # 初始化进程池时指定worker启动时执行的初始化逻辑
    with mp.Pool(1, initializer=_init_worker) as pool:
        try:
            s = pool.starmap(g, [[]] * 1)
        except KeyboardInterrupt:
            print('Done')


if __name__ == "__main__":
    print(os.getpid())
    main()

运行效果说明

  • 按下Ctrl+C时,只有主进程会收到SIGINT触发KeyboardInterrupt,捕获后打印Done
  • 主进程退出with代码块时,会自动给所有工作进程发送SIGTERM,等待进程资源回收后正常退出
  • 全程不会出现工作进程的traceback输出,无进程残留,完全符合优雅退出的要求

注意事项

  • 不要在工作进程中自行捕获KeyboardInterrupt编写退出逻辑,信号处理统一收口到主进程,避免出现僵尸进程、资源泄漏问题
  • 该方案对multiprocessing的所有启动模式(fork/spawn/forkserver)均生效,跨平台兼容
  • 主进程捕获到中断后不需要手动调用pool.terminate(),上下文管理器的默认逻辑已经包含了进程终止、资源回收的完整流程,手动调用反而可能打断正常的清理步骤

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.26 12:45:41