rok*_*rok 8 python deadlock subprocess popen python-3.x
从使用Popen.wait()的文档可以:
当使用 stdout=PIPE 和/或 stderr=PIPE 并且子进程向管道生成足够的输出时死锁,从而阻止等待 OS 管道缓冲区接受更多数据。使用communication() 来避免这种情况。
在交流文档中,它写道:
读取的数据是缓存在内存中的,所以如果数据量很大或者没有限制就不要使用这种方式
如何重现这种有问题的行为并看到使用Popen.communicate()修复它?
死锁意味着持有资源的进程之间会发生一些循环等待并且永远卡住。这里的循环依赖是什么?等待子进程终止的 Python 进程是一种等待。另一个是什么?在下面的场景中,谁在等待什么?
它阻止等待 OS 管道缓冲区接受更多数据
这很简单。
创建一个输出大量文本而不读取输出的进程:
p = subprocess.Popen(["ls","-R"],stdout=subprocess.PIPE)
p.wait()
Run Code Online (Sandbox Code Playgroud)
一段时间后,标准输出管道已满,进程被阻塞。
这是一个死锁情况,因为子进程不能再写入输出,直到它被消耗(即:从不),并且 python 进程等待子进程完成。
为避免死锁,您可以使用读取行循环:
p = subprocess.Popen(["ls","-R"],stdout=subprocess.PIPE)
for line in p.stdout:
# do something with the line
p.wait()
Run Code Online (Sandbox Code Playgroud)
communicate还修复了这个问题,但也修复了更棘手的情况,即输出和错误流都被重定向到单独的流(在这种情况下,上面的幼稚循环仍然可能死锁)。
假设你有一个编译过程
p = subprocess.Popen(["gcc","-c"]+mega_list_of_files,stdout=subprocess.PIPE,stderr=subprocess.PIPE)
Run Code Online (Sandbox Code Playgroud)
现在你想得到这个输出,所以你做:
output = p.stdout.read()
Run Code Online (Sandbox Code Playgroud)
不幸的是,反而弹出了很多错误,在您读取输出流时阻塞了错误流:再次死锁。
尝试改为读取错误流,可能会发生完全相反的情况:大量 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()
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1249 次 |
| 最近记录: |