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

JAu*_*ust 10 python deadlock subprocess stdout 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 
Run Code Online (Sandbox Code Playgroud)

我知道communic()可以给我一个解决这个问题的方法,但我想稍后发出更多的命令.但是communication()有点关闭子进程.

这有什么知道的工作吗?我正在尝试使用python包装器与路由器进行交互.所以我有更多的命令出现,也可以产生一些输出.在那种情况下,如何在不终止子进程的情况下进行读取.

7st*_*tud 13

output = proc.stdout.read()   # Deadlocked here
Run Code Online (Sandbox Code Playgroud)

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

output = proc.stdout.readline()
Run Code Online (Sandbox Code Playgroud)

readline()readline()读取换行符(或EOF)之后,即在读取一行之后将返回.

您的下一个僵局将来自:

  1. 不向newline发送到子进程的输出添加a - 当子进程尝试读取面向行的输入时,即子进程在从stdin读取时查找换行符.

  2. 不是flushing输出,这意味着对方永远不会看到任何要读取的数据,因此另一方会挂起等待数据.

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

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

prog.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)
Run Code Online (Sandbox Code Playgroud)

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

  1. 制作prog.py可执行文件:$ chmod a+x prog.py

  2. 请注意shebang线.

  3. 请注意-u flagshebang行中的python解释器,这意味着程序的所有输出都将是无缓冲的,即它将直接写入 - 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))
Run Code Online (Sandbox Code Playgroud)

回复评论:

你可能会遭受缓冲.大多数程序缓冲输出以提高效率 此外,一些程序将尝试确定它们的stdout是否连接到终端 - 如果是,则程序使用行缓冲,这意味着每次程序输出换行时从缓冲区检索输出并实际写入stdout .这允许使用连接终端的人与程序交互.

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

如果路由器程序是块缓冲,并且输出小于块大小,则实际上没有任何内容写入stdout,因此你的python程序没有任何东西可以读取.

你的python程序不是终端,所以路由器程序可能是块缓冲.正如JF Sebastian在评论中所指出的那样,有一些方法可以让路由器程序认为你的python程序是一个终端,这将导致路由器程序行缓冲,因此你将能够从它的stdout中读取readline().看看pexpect.