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
相关产品推荐
相关产品推荐

