这是我的演示代码。它包含两个脚本。
第一个是main.py
,它将使用子进程模块调用print_line.py
。
第二个是print_line.py
,它向标准输出打印一些内容。
main.py
import subprocess
p = subprocess.Popen('python2 print_line.py',
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
close_fds=True,
shell=True,
universal_newlines=True)
while True:
line = p.stdout.readline()
if line:
print(line)
else:
break
打印行.py
from multiprocessing import Process, JoinableQueue, current_process
if __name__ == '__main__':
task_q = JoinableQueue()
def do_task():
while True:
task = task_q.get()
pid = current_process().pid
print 'pid: {}, task: {}'.format(pid, task)
task_q.task_done()
for _ in range(10):
p = Process(target=do_task)
p.daemon = True
p.start()
for i in range(100):
task_q.put(i)
task_q.join()
之前,print_line.py
是用线程和队列模块编写的,一切都很好。但现在,在更改为多处理模块后,main.py
无法从print_line获取任何输出。我尝试使用 Popen.communicate()
获取输出或在 Popen()
中设置 preexec_fn=os.setsid
。它们都不起作用。
所以,这是我的问题:
为什么子进程无法通过多重处理获得输出?为什么线程可以?
如果我注释掉
stdout=subprocess.PIPE
和stderr=subprocess.PIPE
,输出将打印在我的控制台中。为什么?这是怎么发生的?是否有机会从
print_line.py
获取输出?
最佳答案
好奇。
理论上,这应该按原样工作,但事实并非如此。原因在于缓冲 IO 的深水区。看来,如果不刷新,子进程的子进程的输出可能会丢失。
您有两种解决方法:
一种是在 print_line.py 中使用 flush()
:
def do_task():
while True:
task = task_q.get()
pid = current_process().pid
print 'pid: {}, task: {}'.format(pid, task)
sys.stdout.flush()
task_q.task_done()
这将解决这个问题,因为一旦你向标准输出写入内容,你就会刷新它。
另一种选择是在 main.py 中使用 Python 的 -u
标志:
p = subprocess.Popen('python2 -u print_line.py',
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
close_fds=True,
shell=True,
universal_newlines=True)
-u
将强制 stdin 和 stdout 在 print_line.py
中完全无缓冲,并且 print_line.py
的子级将继承它行为。
这些是解决该问题的方法。如果您对发生这种情况的理论感兴趣,它肯定与子进程终止时未刷新的标准输出丢失有关,但我不是这方面的专家。
关于python - 如何从使用多处理运行脚本的 python2 子进程获取输出?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47626299/