python - 如何在不使用 communicate() 的情况下避免子进程中的死锁

标签 python subprocess stdout deadlock popen

proc = subprocess.Popen(['start'],stdin=subprocess.PIPE,stdout=subprocess.PIPE)
proc.stdin.write('issue commands')
proc.stdin.write('issue more commands')
output = proc.stdout.read()   # Deadlocked here

# Actually I have more commands to issue here 

我知道 communicate() 可以为我提供解决此问题的方法,但我想稍后发出更多命令。但是 communicate() 有点关闭子进程。

这里是否有任何已知的解决方法。我正在尝试使用 python 包装器与路由器进行交互。所以我有更多的命令出现,它们也可以产生一些输出。在那种情况下,我如何在不终止子进程的情况下阅读。

最佳答案

output = proc.stdout.read()   # Deadlocked here

该行导致死锁,因为 read() 在读取 EOF 之前不会返回,EOF 在另一端关闭其标准输出时发送(例如,当子进程终止时)。相反,您想阅读面向行的输入:

output = proc.stdout.readline()

readline() 将在读取换行符(或 EOF)后返回,即在 readline() 读取 line 后返回。

您的下一个死锁将由以下任一原因导致:

  1. 不向发送给子进程的输出添加换行符——当子进程试图读取面向行的输入时,即子进程在从标准输入读取时正在寻找换行符.

  2. 没有flushing输出,这意味着另一端看不到任何数据可读,因此另一端挂起等待数据。

为了提高效率,当您写入文件(包括 stdout、stdin)时,输出是缓冲,这意味着 python 不会实际将输出写入文件,而是欺骗您并将输出存储在一个列表(称为缓冲区)。然后当列表增长到一定大小时,python一次性将所有输出写入文件,这比一次写一行效率更高。

不向发送给子进程的所有输出添加换行符可以很容易地纠正;但是,发现所有需要刷新缓冲区的地方更加困难。这是一个例子:

程序.py:

#!/usr/bin/env python3.4 -u

import sys

print('What is your name?') 
name = input()
print(name)

print("What is your number?")
number = input()
print(number)

假设您想用另一个程序驱动该程序?

  1. 使 prog.py 可执行:$ chmod a+x prog.py

  2. 注意 shebang 行。

  3. 请注意 shebang 行中 python 解释器的 -u 标志,这意味着 that 程序的所有输出都将是无缓冲的,即它将是直接写入 stdout——不累积在缓冲区中。


import subprocess as sp

child = sp.Popen(
    ['./prog.py'],
    stdin = sp.PIPE,
    stdout = sp.PIPE
)

print_question = child.stdout.readline().decode('utf-8')  #PIPE's send a bytes type, 
                                                          #so convert to str type
name = input(print_question)
name = "{}\n".format(name)
child.stdin.write(
    name.encode('utf-8') #convert to bytes type
)
child.stdin.flush()

print_name = child.stdout.readline().decode('utf-8')
print("From client: {}".format(name))

print_question = child.stdout.readline().decode('utf-8')

number = input(print_question)
number = "{}\n".format(number)
child.stdin.write(
    number.encode('utf-8')
)
child.stdin.flush()

print_number = child.stdout.readline().decode('utf-8')
print("From client: {}".format(print_number))

对评论的回应:

您可能缓冲不足。大多数程序缓冲输出以提高效率。此外,某些程序会尝试确定它们的标准输出是否连接到终端——如果是,则该程序使用行缓冲,这意味着从缓冲区检索输出并实际写入标准输出每次程序输出一个换行符。这允许使用连接的终端的人与程序进行交互。

另一方面,如果程序的 stdout 没有连接到终端,程序将阻塞缓冲区,这意味着只有在缓冲区连接到终端后,输出才会从缓冲区中检索并实际写入 stdout。增长到特定大小,比如 4K 数据。如果程序是 block 缓冲且输出小于 4K,则实际上没有任何内容写入标准输出。 block 缓冲允许其他计算机程序以更高的效率检索程序的输出。

如果路由器程序是 block 缓冲,并且它的输出小于 block 大小,那么实际上没有任何内容写入标准输出,因此您的 python 程序无法读取任何内容。

您的 python 程序不是终端,因此路由器程序可能是 block 缓冲。正如 J.F. Sebastian 在评论中指出的那样,有一些方法可以让路由器程序认为您的 python 程序是一个终端,这将导致路由器程序 line buffer,因此您将能够使用 readline() 从其标准输出中读取。查看预期。

关于python - 如何在不使用 communicate() 的情况下避免子进程中的死锁,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33886406/

相关文章:

python - 如何在取平均值的同时合并日期时间列上的值

linux - 如何从 lxterminal 中将输出和错误重定向到外部文件?

java - Weblogic 托管服务器未正确记录错误

python - 像 Python 中的脚本一样的设置向导

python - 无法使用 GLFW 作为后端运行 Glumpy 示例应用程序

python - python中的质量字符串替换?

C 将标准输出恢复到终端

分离进程的Pythonic方式?

python - 从 Python 运行 Matlab M 函数

Python 通信 vs shell=True