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

如何通过KeyboardInterrupt优雅终止multiprocessing.Pool.map函数?

问题:如何通过单次Ctrl+C终止multiprocessing.Pool.map()?

我尝试用KeyboardInterrupt优雅终止multiprocessing.Pool.map()函数,相关代码如下:

import multiprocessing
import time


def func(num):
    try:
        while True:
            print(f'Process num {num}')
            time.sleep(30)
    except KeyboardInterrupt:
        print(f'Process {num} was interrupted')
    finally:
        print('Cleaning up the thread')


def main():
    pool = multiprocessing.Pool()

    try:
        pool.map(func, range(9))
    except KeyboardInterrupt:
        print('Main process was interrupted')
        pool.Close()

    finally:
        print('Cleaning up main')


if __name__ == '__main__':
    main()

我的设备有8个逻辑核心,当传入range(8)时,按Ctrl+C后能正常终止所有进程,输出符合预期;但传入range(9)时,第一次按Ctrl+C仅终止正在运行的8个进程,Pool.map仍会生成新的进程(进程8),需要再次按Ctrl+C才会触发异常。请问有没有办法仅按一次Ctrl+C就停止Pool.map()?


解决方案

问题根源

当Pool.map处理的任务数超过进程池大小(你的设备是8)时,第一次Ctrl+C会终止当前正在运行的8个进程,但主进程此时还在等待新的进程启动来处理剩余任务(第9个),直到新进程启动后再次收到信号才会触发主进程的KeyboardInterrupt。

解决方法

方法1:用pool.terminate()替代pool.close()

pool.close()仅阻止新任务提交,但不会终止待启动的任务;pool.terminate()会立即终止所有进程,无论任务是否完成。修改主进程的异常处理逻辑:

def main():
    pool = multiprocessing.Pool()

    try:
        pool.map(func, range(9))
    except KeyboardInterrupt:
        print('Main process was interrupted')
        pool.terminate()  # 替换为terminate,立即终止所有进程
    finally:
        pool.join()  # 等待所有子进程退出后再执行清理
        print('Cleaning up main')

方法2:注册全局信号处理函数

在主进程中捕获SIGINT信号,一旦收到就立即终止进程池:

import multiprocessing
import time
import signal

def func(num):
    try:
        while True:
            print(f'Process num {num}')
            time.sleep(30)
    except KeyboardInterrupt:
        print(f'Process {num} was interrupted')
    finally:
        print('Cleaning up the thread')

def handle_interrupt(signum, frame):
    print('Received interrupt signal, terminating pool')
    global pool
    pool.terminate()
    raise KeyboardInterrupt  # 重新抛出异常,让主进程进入finally块

def main():
    global pool
    pool = multiprocessing.Pool()
    signal.signal(signal.SIGINT, handle_interrupt)  # 注册信号处理

    try:
        pool.map(func, range(9))
    except KeyboardInterrupt:
        print('Main process was interrupted')
    finally:
        pool.join()
        print('Cleaning up main')

if __name__ == '__main__':
    main()

注意事项

  • pool.terminate()会强制终止子进程,可能导致子进程的finally块无法执行。如果需要子进程完成清理,可在信号处理中先给子进程发送中断信号,短暂等待后再终止进程池,但这种方式可靠性有限。
  • pool.join()必须在pool.close()或pool.terminate()之后调用,确保主进程等待所有子进程完全退出。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 07:05:44