当我使用multiprocessing.Queue.get时,由于EINTR,我有时会得到一个异常。
我肯定知道有时这种情况没有充分理由(我在tmux buffr中打开另一个窗格),在这种情况下我想继续工作并重试该操作。
我可以想象在其他一些情况下,错误是由于一个很好的理由,我应该停止运行或修复一些错误。
我如何区分这两者?
提前致谢
答案 0 :(得分:14)
当应用程序在等待其他输入时收到信号时,可以从许多系统调用返回EINTR
错误。通常这些信号可能非常温和并且已经由Python处理,但底层系统调用仍然最终被中断。在进行C / C ++编码时,这就是为什么你不能完全依赖sleep()
之类的函数的原因之一。 Python库有时会在内部处理这个错误代码,但显然在这种情况下它们不是。
您可能有兴趣阅读讨论此问题的this thread。
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程序中的EINTR
,除非你知道你正在等待一个特定的信号(例如SIGHUP
)并且你已经安装了一个信号处理程序设置一个标志并依赖于代码的主体来获取标志。在这种情况下,如果收到EINTR
,则可能需要中断循环并检查信号标记。
但是,如果您没有使用任何信号处理,那么您应该能够忽略EINTR
并重复您的操作 - 如果Python本身需要对信号执行某些操作,那么它应该已经处理过信号处理程序。
答案 1 :(得分:6)
老问题,现代解决方案:从Python 3.5开始,精彩的PEP 475 - Retry system calls failing with EINTR已经实现并为您解决了问题。这是摘要:
标准库中提供的系统调用包装器在
EINTR
失败时应自动重试,以减轻应用程序代码的负担。通过系统调用,我们指的是标准C库公开的与I / O或其他系统资源处理有关的函数。
基本上,系统会捕获并重试一段失败的EINTR
代码,因此您不必再处理它了。如果您要定位旧版本,while True
循环仍然是可行的方法。但请注意,如果您使用的是Python 3.3或3.4,则可以捕获专用异常InterruptedError
,而不是捕获IOError
并检查EINTR
。