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

Python3触发KeyboardInterrupt时如何确保完成当前循环再退出

Python实现循环内foo/bar成对执行不被Ctrl+C中断的方案

核心要求是单轮循环里从foo(i)开始到bar(i)结束的整段逻辑是不可被KeyboardInterrupt打断的原子块,中断只会在两轮循环的间隙被响应,不管Ctrl+C是在foo运行时、foo和bar之间、还是bar运行时按下,都要等当前轮次全部跑完再退出。

方案1:自定义SIGINT信号处理(最可靠,完全满足需求)

Python默认收到Ctrl+C发送的SIGINT信号时,会在任意字节码执行间隙抛出KeyboardInterrupt,不管你代码写到哪都可能被打断。最稳妥的解决方式是临时替换信号处理逻辑:收到信号时先打个标记,不立刻抛异常,等整轮循环逻辑跑完再检查标记、响应中断。
直接上代码:

import signal

# 中断标记,记录是否收到过Ctrl+C
interrupted = False

def handle_sigint(signum, frame):
    global interrupted
    interrupted = True

# 先保存原来的SIGINT处理逻辑,退出前恢复
original_handler = signal.getsignal(signal.SIGINT)
# 注册我们自己的临时处理函数
signal.signal(signal.SIGINT, handle_sigint)

for i in range(10**6):
    # 下面这整段执行期间,按Ctrl+C只会改标记,不会打断执行
    foo(i)
    print("foo executed with parameter", i)
    bar(i)
    print("bar executed with parameter", i)

    # 整轮跑完再检查有没有收到中断
    if interrupted:
        # 恢复原信号处理
        signal.signal(signal.SIGINT, original_handler)
        # 主动抛出键盘中断,和原生触发的行为完全一致
        raise KeyboardInterrupt

这个方案没有逻辑漏洞:不管你是在foo跑一半、打印foo日志的时候、还是bar跑一半按Ctrl+C,都只会把interrupted标记设为True,当前轮次的代码会完整跑完,之后才会触发中断退出,完全匹配需求。


方案2:try...except包裹整轮逻辑(仅适合简单场景)

如果你不想操作信号模块,也可以用try块捕获整轮迭代内的中断,捕获到之后补完剩余逻辑再重抛。但这个方案有明显局限性:如果中断是在foo执行过程中触发的,foo本身会被异常打断,没法保证foo执行完成,只能覆盖中断发生在foo和bar之间的场景。
代码示例:

for i in range(10**6):
    try:
        foo(i)
        print("foo executed with parameter", i)
        bar(i)
        print("bar executed with parameter", i)
    except KeyboardInterrupt:
        # 兜底补跑bar,注意如果中断是在foo内部触发的,foo已经被打断,不符合要求
        bar(i)
        print("bar executed with parameter", i)
        raise

如果你的foo和bar都是执行速度极快、内部几乎不会被信号打断的小逻辑,这个方案也能用,但生产场景优先选方案1。

避坑提醒

不要只把try/except套在foo和bar中间的打印语句上,那样完全覆盖不到两个函数执行过程中收到中断的场景,实现不了要求的原子性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 07:24:25