在Python中处理IOError: [Errno 4] Interrupted system call错误的正确方法(由multiprocessing.Queue.get引发)是什么?

13
当我使用multiprocessing.Queue.get时,有时会由于EINTR而出现异常。我知道有时候这种情况是没有好的原因的(比如我在tmux缓冲区中打开了另一个窗格),在这种情况下,我想要继续工作并重试操作。但我也可以想象,在其他一些情况下,错误可能是由于好的原因而发生的,我应该停止运行或修复一些错误。那么我该如何区分这两种情况呢?谢谢!

在执行get操作之前,你应该确保队列不为空:if not queue.empty(): queue.get() - Inbar Rose
我相信如果队列为空,它只会阻塞调用。不是吗?无论如何,我不认为这是错误的原因。 - Shwouchk
2个回答

18

EINTR错误可能在等待其他输入时应用程序收到信号时从许多系统调用中返回。通常,这些信号可能会被Python处理得很好,并且已经被处理了,但是底层系统调用仍然被中断。在进行C/C++编码时,这就是为什么您不能完全依靠像sleep()之类的函数的原因之一。Python库有时会在内部处理此错误代码,但在这种情况下显然没有。

您可能有兴趣阅读此线程,其中讨论了这个问题。

处理EINTR的一般方法是简单地处理错误并重试操作-通过队列上的get()方法这样做应该是安全的。可以使用类似以下的内容,将队列作为参数传递并替换对队列上get()方法的使用:

import errno

def my_queue_get(queue, block=True, timeout=None):
    while True:
        try:
            return queue.get(block, timeout)
        except IOError, e:
            if e.errno != errno.EINTR:
                raise

# Now replace instances of queue.get() with my_queue_get(queue), with other
# parameters passed as usual.
通常在Python程序中,除非你知道自己在等待特定信号(例如 SIGHUP)并且安装了一个信号处理器来设置标志并依赖于代码主体来获取该标志,否则你不必担心EINTR。在这种情况下,如果收到EINTR,你可能需要退出循环并检查信号标志。
但是,如果你没有使用任何信号处理,则可以忽略EINTR并重复操作 - 如果Python本身需要对信号进行处理,则应在信号处理程序中已经处理过它。

我不能点赞,因为我的声望太低了...感谢您的回答! - Shwouchk
没有太多的知识,不太清楚该怎么做,但我用 my_queue_get(result) 替换了 result.get()。我相信这就是应该做的,以防其他人想知道如何使用代码。 - user-2147482637
是的,之前回答有点含糊不清 - 我已经更新了答案。 - Cartroo

8

老问题,现代解决方案:从Python 3.5开始,神奇的PEP 475 - Retry system calls failing with EINTR已经实现并为您解决了这个问题。以下是摘要:

标准库中提供的系统调用包装器应在失败时自动重试,以减轻应用程序代码的负担。

通过系统调用,我们指的是与I/O或处理其他系统资源有关的标准C库公开的函数。

基本上,当某段代码因出现EINTR而失败时,系统会捕获并自动重试,因此您不必再去处理它。如果您的目标是旧版本,则仍可以使用while True循环。不过需要注意的是,如果您使用的是Python 3.3或3.4,可以捕获专用的异常InterruptedError而不是捕获IOError并检查EINTR


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接