python - Python asyncio子进程连续写入stdin和读取stdout/stderr
问题描述
我目前正在执行 python3 asyncio 中的子进程任务。我的代码只是写入标准输入并同时读取标准输出/标准错误:
import asyncio
async def read_stdout(stdout):
print('read_stdout')
while True:
buf = await stdout.read(10)
if not buf:
break
print(f'stdout: { buf }')
async def read_stderr(stderr):
print('read_stderr')
while True:
buf = await stderr.read()
if not buf:
break
print(f'stderr: { buf }')
async def write_stdin(stdin):
print('write_stdin')
for i in range(100):
buf = f'line: { i }\n'.encode()
print(f'stdin: { buf }')
stdin.write(buf)
await stdin.drain()
await asyncio.sleep(0.5)
async def run():
proc = await asyncio.create_subprocess_exec(
'/usr/bin/tee',
stdin=asyncio.subprocess.PIPE,
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.PIPE)
await asyncio.gather(
read_stderr(proc.stderr),
read_stdout(proc.stdout),
write_stdin(proc.stdin))
asyncio.run(run())
它工作得很好,但我在Python3 文档页面上看到了一个警告:
Warning
使用communicate()
方法而不是process.stdin.write()
,await process.stdout.read()
或await process.stderr.read
。这避免了由于流暂停读取或写入并阻塞子进程而导致的死锁。
这是否意味着上面的代码在某些情况下会陷入僵局?如果是这样,如何在没有死锁的python3 asyncio中连续写入stdin
和读取stdout
/ ?stderr
非常感谢。
解决方案
该警告是从常规子进程模块中继承而来的,并警告不要尝试实现看起来完全正确的简单通信的幼稚代码,例如:
# write the request to the subprocess
await proc.stdin.write(request)
# read the response
response = await proc.stdout.readline()
如果子进程在读取整个请求之前开始写入响应,这可能会导致死锁。如果响应足够大,子进程将阻塞,等待父进程读取其中的一些并在管道缓冲区中腾出空间。但是,父级不能这样做,因为它仍在写入响应并在开始读取之前等待写入完成。因此,孩子等待父母阅读(部分)其响应,而父母等待孩子完成接受请求。由于双方都在等待对方的当前操作完成,这是一个死锁。
您的代码没有这个问题,仅仅是因为您的读取和写入是并行执行的。由于读者从不等待作者,反之亦然,因此(那种)死锁没有机会。如果你看一下它communicate
是如何实现的,你会发现,除了一些调试日志之外,它的工作方式与你的代码非常相似。
推荐阅读
- resources - apiplatform 无法访问公共资源
- windows - Git 扩展:如何使用全局变量设置 URL
- java - 可展开列表视图中的按钮 onclick
- .net - 如何在对内部 API 的调用之间保留来自外部 API 的 API 访问令牌
- python - 由于四舍五入,train_test_split 会产生意外的样本量
- apache-kafka - 关闭所有代理并重新启动非协调代理后,Kafka 集群重选失败
- reactjs - 继续我在 SurveyJs 中留下的问题
- xml - Android studio 无法在activity_main 中执行drawable
- c++ - 参数和返回类型中的模板类型推导
- c++ - How to pass values between C++20 Coroutines?