使用Popen.wait()时重现死锁

时间:2018-04-09 08:24:04

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

使用Popen.wait()的文档可以:

  使用stdout = PIPE和/或stderr = PIPE和孩子时出现

死锁   进程为管道生成足够的输出,以阻止等待   为OS管道缓冲区接受更多数据。使用communic()来避免   这一点。

在沟通docs中写道:

  

读取的数据缓冲在内存中,因此如果使用此方法,请不要使用此方法   数据大小很大或无限制

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

死锁意味着在持有资源的进程之间发生一些循环等待并且永远停滞不前。 这里的循环依赖是什么?等待子进程终止的Python进程是一个等待。另一个是什么?谁在等待下面的情况?

  

它阻止等待OS管道缓冲区接受更多数据

1 个答案:

答案 0 :(得分:1)

这很容易。

创建一个输出大量文本且读取输出的过程:

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

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

这是一个死锁情况,因为子进程在消耗之前不能再写入输出(这是:never),并且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使用多线程来处理输出&错误流同时,分离,没有阻塞的风险。