Multiprocessing场景下如何通过键盘中断(Ctrl+C)正确取消运行中的进程
故障根因
multiprocessing.Queue的put()为默认阻塞方法:当队列缓冲区被填满后,方法会永久阻塞等待空闲空间,原代码仅向队列写入数据没有消费逻辑,队列满后子进程会卡在queue.put(msg)行,永远不会回到循环头部判断running_flag.is_set(),自然无法响应退出信号。- 信号处理逻辑覆盖不全:
Ctrl+C触发的SIGINT会默认发送给同进程组的所有进程,原子进程未自定义SIGINT处理逻辑,收到信号后会直接抛出KeyboardInterrupt异常终止,不会触发主进程的清理逻辑,部分场景下会导致主进程join()调用永久阻塞。 - 全局
running_flag未初始化:原代码直接调用running_flag.set(),但提前没有创建mp.Event()实例,属于未定义行为,部分运行环境会直接抛出属性错误。
修复方案
调整后的可稳定退出代码如下:
import multiprocessing as mp import signal # 提前初始化全局运行标识 running_flag = mp.Event() def func(running_flag: mp.Event, queue: mp.Queue): # 子进程忽略SIGINT,统一由主进程处理退出 signal.signal(signal.SIGINT, signal.SIG_IGN) counter = 0 while running_flag.is_set(): counter += 1 msg = "\nCounter:" + str(counter) try: # put增加超时,避免队列满后永久阻塞 queue.put(msg, timeout=0.5) except: # 队列满时跳过本次写入,继续判断运行标识 continue def manage_ctrlC(*args): running_flag.clear() def main(): signal.signal(signal.SIGINT, manage_ctrlC) processes = [] # 队列设置最大长度,避免无限制占用内存 queue_y = mp.Queue(maxsize=100) queue_z = mp.Queue(maxsize=100) running_flag.set() processes.append(mp.Process(target=func, args=(running_flag, queue_y))) processes.append(mp.Process(target=func, args=(running_flag, queue_z))) try: for p in processes: p.start() # 主进程增加消费逻辑,也可以根据业务需要替换为其他逻辑 while running_flag.is_set(): if not queue_y.empty(): print(queue_y.get()) if not queue_z.empty(): print(queue_z.get()) finally: running_flag.clear() for p in processes: # join设置超时,避免异常场景下永久阻塞 p.join(timeout=1) if p.is_alive(): # 子进程未正常退出时强制终止 p.terminate() return if __name__ == "__main__": main()
内容的提问来源于stack exchange,提问作者Marttinho
相关产品推荐
相关产品推荐

