从损坏的管道读取时,管道 Python 脚本占用 100% 的 CPU

Posted

技术标签:

【中文标题】从损坏的管道读取时,管道 Python 脚本占用 100% 的 CPU【英文标题】:Piped Python script takes 100% of CPU when reading from broken pipe 【发布时间】:2012-09-10 16:58:21 【问题描述】:

我有两个 Python 脚本在 Ubuntu Linux 机器上运行。第一个将其所有输出发送到标准输出,第二个从标准输入读取。它们通过一个简单的管道连接,例如:

./step1.py <some_args> | ./step2.py <some_other_args>

step2 的作用是在无限循环中读取输入行并对其进行处理:

while True:
    try:
        l = sys.stdin.readline()
        # processing here

Step1 时常崩溃。当这种情况发生时(不确定是否总是,但至少在几次情况下)不是崩溃/停止,而是 step2 变得疯狂并开始占用 100% 的 CPU,直到我手动杀死它。

为什么会发生这种情况?如何使 step2 更健壮,以便在管道损坏时停止?

谢谢!

【问题讨论】:

因为如果管道发生故障,您基本上拥有while True:pass,它将垄断cpu add 和except: break; 以退出循环......或except:time.sleep(100) 或减轻负载的东西 你发现了什么异常? 【参考方案1】:

其他人已经解释了为什么在某些情况下你会陷入无限循环。

在第二个(阅读)脚本中,可以使用成语:

for line in sys.stdin:
    process(line)

这样您就不会陷入无限循环。此外,您实际上并没有在第二个脚本中显示您尝试捕获的异常,但我猜您会不时遇到“破管”错误,您可以并且应该捕获如此处所述:How to handle a broken pipe (SIGPIPE) in python?

整个方案可能如下所示:

try:
    for line in sys.stdin:
        process(line)
except IOError, e:
    if e.errno == errno.EPIPE:
        # EPIPE error
    else:
        # Other error

【讨论】:

【参考方案2】:

当 step1 终止时,您有一个 while 循环,其中包含一个 try 语句,该语句将引发异常。因此,您将使用 100% 的 CPU 不断尝试并失败,因为 readline 在抛出异常时不会阻塞。

要么使用time.sleep 为读取添加时间延迟,或者更好的是,注意 readline 抛出的错误并捕获当 step1 停止并退出程序而不是尝试从死机读取时抛出的特定错误管道。

您可能需要在管道为空时使用睡眠操作符,并在管道死亡时退出,但是在每种情况下都会抛出哪个异常以及什么消息,我将作为练习留给您确定。在这种情况下,睡眠运算符不是必需的,但它可以避免在无用工作上导致 CPU 使用率过高的其他情况。

【讨论】:

以上是关于从损坏的管道读取时,管道 Python 脚本占用 100% 的 CPU的主要内容,如果未能解决你的问题,请参考以下文章

带有标准输入的 subprocess.Popen.communicate() 的管道损坏

从命名管道、C 程序(编写器)和 Python(读取器)获取额外数据

Python 和 asyncio:封闭的命名管道始终可供读取

Win32 匿名管道在第一次读取后损坏

持久的子进程管道 - 没有读取标准输出

ALSA:无法从欠载中恢复,准备失败:管道损坏