【问题标题】:Reproducing deadlock while using Popen.wait()使用 Popen.wait() 时重现死锁
【发布时间】:2018-09-18 14:06:55
【问题描述】:

来自使用Popen.wait() 的文档可能:

使用 stdout=PIPE 和/或 stderr=PIPE 和子时出现死锁 进程向管道生成足够的输出,从而阻止等待 让操作系统管道缓冲区接受更多数据。使用communicate() 来避免 那个。

在交流docs 中写道:

读取的数据缓存在内存中,所以如果 数据量大或无限制

如何重现这种有问题的行为并查看使用Popen.communicate() 修复它?

死锁意味着持有资源的进程之间发生了一些循环等待,并且永远卡住了。 这里的循环依赖是什么?等待子进程终止的 Python 进程是一个等待。另一个是什么?谁在下面的场景中等待什么?

它阻塞等待操作系统管道缓冲区接受更多数据

【问题讨论】:

  • 如果您遇到了死锁,通信将不会“修复”它。您应该首先使用通信,以避免陷入僵局。
  • 实际上我遇到过这样的情况,每次运行时使用等待的代码都会卡住,直到它被修改为使用通信并且不再卡住了。所以我想重现死锁的情况,看看沟通解决了它

标签: python python-3.x subprocess deadlock popen


【解决方案1】:

这很容易。

创建一个输出大量文本的进程并且不要读取输出:

p = subprocess.Popen(["ls","-R"],stdout=subprocess.PIPE)
p.wait()

一段时间后,标准输出管道已满,进程被阻塞。

这是一个死锁的情况,因为子进程在被消耗之前无法再写入输出(即:从不),python进程等待子进程完成。

为避免死锁,您可以使用读取行循环:

p = subprocess.Popen(["ls","-R"],stdout=subprocess.PIPE)
for line in p.stdout:
    # do something with the line
p.wait()

communicate 也修复了这个问题,但也修复了输出 错误流都被重定向到 单独的流 的更棘手的情况(在这种情况下,上面的幼稚循环仍然可能死锁)。

假设你有一个编译过程

p = subprocess.Popen(["gcc","-c"]+mega_list_of_files,stdout=subprocess.PIPE,stderr=subprocess.PIPE)

现在你想从这个得到输出,所以你这样做:

output = p.stdout.read()

不幸的是,反而会弹出很多错误,在您读取输出流时阻塞错误流:再次死锁。

尝试改为读取错误流,可能会发生完全相反的情况:大量 stdout 输出阻塞了您的进程。

communicate 使用多线程能够同时处理输出和错误流并将它们分开,没有阻塞的风险。唯一需要注意的是,您无法实时控制进程输出/打印程序输出:

p = subprocess.Popen(["gcc","-c"]+mega_list_of_files,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
output,error = p.communicate()
return_code = p.wait()

【讨论】:

  • 谢谢。哪个实体应该消耗已满的输出?父进程?也不明白沟通如何解决它。它在其文档中写道do not use this method if the data size is large or unlimited
  • 是的,你的 python 进程。当然,如果输出是无限的,则通信永远不会结束并消耗所有内存。在这种情况下,例如,您必须在遇到给定行时终止该进程。在这种情况下,您必须逐行阅读。
  • 很好答案。似乎有一个想法,即在使用PIPE 时必须始终使用communicate(),正如您指出的那样,情况并非如此。文档实际上并没有错,因为读取行循环会阻止 ,并且子进程会向管道生成足够的输出,从而阻塞....
  • 如果您使用communicate(),有些事情是您无法做到的,例如在进程输出数据时逐行读取和实时执行操作。最简单的例子:同时打印和记录到控制台,并检测一个特殊的行来终止进程(特别是如果进程没有停止就永远运行,这会禁止使用通信……)。而且大多数时候合并输出和错误流是可以的,它避免了死锁(如果输出当然被消耗)
猜你喜欢
  • 1970-01-01
  • 2023-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-21
  • 2019-06-13
相关资源
最近更新 更多