Ant*_*ent 11 python subprocess python-3.x
我在Python工作,我想找到一个工作流程,使两个流程(主流程和子流程)能够相互通信.通过这种方式,我的意思是主进程将一些数据发送到子进程(可能是通过写入子进程的 stdin)的能力以及子进程将一些数据发送回主进程的能力.这也意味着两者都可以读取发送给他们的数据(我正在考虑从stdin读取).
我试图使用子进程库,但它似乎打算使用旨在只提供一次输出然后终止的进程,而我想动态交换数据并仅在这样的命令时关闭子进程收到了.
我在StackOverflow上阅读了很多关于与我的问题密切相关的问题的答案,但是我没有找到令人满意的答案,因为这些答案的意思与我的不同之处在于一个重要的细节:我需要我的主要流程来能够根据需要动态地与其子进程交换数据,而不仅仅是一次,这反过来意味着子进程应该运行,直到它从主进程接收到终止的某个命令.
我愿意使用第三方库,但如果你提出一个完全基于Python标准库的解决方案会更好.
看起来管道可能是适合您的用例的选择。但要注意的是,正常情况下,读端和写端都希望分别写入或读取数据。还要确保您不会对缓冲感到惊讶(没有任何结果,因为除非在预期边界上,否则缓冲区不会自动刷新,除非进行相应设置)。
如何在两个进程之间使用两个管道(它们是单向的)的基本示例:
import os
def child():
"""This function is executed in a child process."""
infile = os.fdopen(r1)
outfile = os.fdopen(w2, 'w', buffering=1)
for line in infile:
if line.rstrip() == 'quit':
break
print(line.upper(), end='', file=outfile)
def parent():
"""This function is executed in a parent process."""
outfile = os.fdopen(w1, 'w', buffering=1)
infile = os.fdopen(r2)
print('Foo', file=outfile)
print(infile.readline(), end='')
print('bar', file=outfile)
print(infile.readline(), end='')
print('quit', file=outfile)
(r1, w1) = os.pipe() # for parent -> child writes
(r2, w2) = os.pipe() # for child -> parent writes
pid = os.fork()
if pid == 0:
child() # child code runs here
elif pid > 0:
parent() # parent code runs here
os.waitpid(pid, 0) # wait for child
else:
raise RuntimeError("This should not have happened.")
Run Code Online (Sandbox Code Playgroud)
事实上,使用它会更容易、更实用subprocess,而且您可能想运行另一个程序。前者需要被告知不要关闭管道文件描述符,后者需要管道文件描述符是可继承的(没有O_CLOEXEC设置标志)。
儿童计划:
import os
import sys
infile = os.fdopen(int(sys.argv[1]))
outfile = os.fdopen(int(sys.argv[2]), 'w', buffering=1)
for line in infile:
if line.rstrip() == 'quit':
break
print(line.upper(), end='', file=outfile)
Run Code Online (Sandbox Code Playgroud)
家长计划:
import os
import subprocess
(r1, w1) = os.pipe2(0) # for parent -> child writes
(r2, w2) = os.pipe2(0) # for child -> parent writes
child = subprocess.Popen(['./child.py', str(r1), str(w2)], pass_fds=(r1, w2))
outfile = os.fdopen(w1, 'w', buffering=1)
infile = os.fdopen(r2)
print('Foo', file=outfile)
print(infile.readline(), end='')
print('bar', file=outfile)
print(infile.readline(), end='')
print('quit', file=outfile)
child.wait()
Run Code Online (Sandbox Code Playgroud)
如果子程序不需要标准输入或标准输出,则可以使用它们分别从子程序中获取信息。这甚至会更简单。
儿童计划:
import sys
for line in sys.stdin:
if line.rstrip() == 'quit':
break
print(line.upper(), end='', flush=True)
Run Code Online (Sandbox Code Playgroud)
家长计划:
import os
import subprocess
(r1, w1) = os.pipe2(0) # for parent -> child writes
(r2, w2) = os.pipe2(0) # for child -> parent writes
child = subprocess.Popen(['./child.py'], stdin=r1, stdout=w2)
outfile = os.fdopen(w1, 'w', buffering=1)
infile = os.fdopen(r2)
print('Foo', file=outfile)
print(infile.readline(), end='')
print('bar', file=outfile)
print(infile.readline(), end='')
print('quit', file=outfile)
child.wait()
Run Code Online (Sandbox Code Playgroud)
如前所述,它并不是真正特定于 Python 的,这些只是关于如何使用管道作为一种选项的粗略提示。
您希望为标准输入和输出创建一个Popen对象,subprocess.PIPE并使用其文件对象进行通信——而不是使用像 那样的cantrips之一run(以及更老的、更具体的像check_output)。挑战在于避免死锁:很容易陷入这样一种情况:每个进程都在尝试写入,管道缓冲区已满(因为没有人正在读取它们),并且一切都挂起。您还必须记住flush在这两个进程中,以避免请求或响应卡在file对象的缓冲区中。
Popen.communicate提供以避免这些问题,但它仅支持单个字符串(而不是正在进行的对话)。传统的解决方案是select,但它也适用于使用单独的线程来发送请求和读取结果。(这是尽管有 GIL 仍然使用 CPython 线程的原因之一:每个线程都可以运行,而另一个线程被阻塞,因此很少有争用。)当然,同步是一个问题,您可能需要做一些努力使多线程客户端表现得像一个简单的、同步的外部函数调用。
请注意,两个进程都需要flush,但是如果其中一个实现了这种非阻塞 I/O就足够了;一个通常在启动另一个的过程中完成这项工作,因为这是已知必要的(并且此类程序是例外)。
| 归档时间: |
|
| 查看次数: |
588 次 |
| 最近记录: |