【问题标题】:Break out of while loop handling keybreak exception correctly跳出while循环正确处理keybreak异常
【发布时间】:2021-04-10 23:05:19
【问题描述】:

我需要实现一个功能来监控正在运行的作业的状态,直到它完成。像这样的:

def monitor_job(job_id):
    ended = False
    while not ended:
        ended = check_if_ended(job_id)
        time.sleep(5)

    print('ended')

现在我想允许用户使用 Ctrl-C 中断循环,在这种情况下,我想优雅地调用 stop_job() 函数来停止工作。但是这个函数是异步的,它只是提交一个停止作业的请求,这可能需要一些时间才能完成。因此,在这个电话之后,我想继续 while 循环并等待工作结束。这是一个尝试:

def monitor_job(job_id):
    keybreaks = False
    ended = False
    while not ended:

        # (...)

        try:
            ended = check_if_ended(job_id)
            time.sleep(5)
        except KeyboardInterrupt:
            # break after a second keybreak
            if keybreaks:
                raise
            keybreaks = True
            stop_job(job_id)

    print('ended')

我在这个解决方案中不喜欢的是,如果偶然在 try 之外触发了 Ctrl-C(例如在 (...)),那么将不会捕获异常,并且作业不会优雅地停止。

有没有好的方法来正确处理这个问题,避免这种时间问题?

我想到了一些解决方案,例如:在 except 块内复制 while 循环,这并不优雅;或者将try 放在while 循环之外,然后在except 中回调monitor_job(),但是在函数调用期间可能会发生Ctrl-C。

【问题讨论】:

  • 如果您没有在try(...) 所在的位置)之外放置任何代码,那么它应该始终被捕获。因此,只需将 whiletry 语句之间的所有内容移动到 try 块中。
  • 你的意思是它不太可能发生或不可能发生?我想让它原则上不可能
  • 这“只是”非常不可能,但几乎不可能。因为与其他所有内容相比,您的睡眠时间是如此之长,所以sigint 不太可能在 try 块之外命中。对于“原则上不可能”,请参阅下面的答案。祝你有美好的一天!

标签: python loops exception atomic


【解决方案1】:

Ctrl-C 确实有可能发生在(...)。但这只是非常!很小的机会。

如果您想绝对确定可以使用以下 sn-p:

def monitor_job(job_id, first_call=True):
    ended = False
    try:
        if first_call:
            monitor_job(job_id, first_call=False)
        while not ended:
            ended = check_if_ended(job_id)
            time.sleep(5)
    except KeyboardInterrupt:
        if first_call:
            raise

它还使用了像您建议的递归,但不是在 except 块中而是在 try 块中。所以当你点击Ctrl-C时,你首先退出对monitor_job(first_call=False)的内部调用,只有在第二个Ctrl-C之后你才会退出外部函数。

这是我能想到的最优雅的解决方案。

【讨论】:

    猜你喜欢
    • 2018-11-13
    • 2014-11-11
    • 1970-01-01
    • 2013-09-21
    • 1970-01-01
    • 2016-11-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多