我有一些使用子进程的非异步代码
import subprocess
import signal
p = subprocess.Popen(['/bin/true'], stdout=subprocess.PIPE)
# ... do something else here ...
# The process may or may not have finished yet.
# For the sake of this test, let us ensure a finish here
# by waiting for EOF on a pipe.
p.stdout.read()
p.terminate()
我尝试将其迁移到asyncio。但是,.terminate()
调用会引发ProcessLookupError
import asyncio
import asyncio.subprocess
import signal
async def main():
p = await asyncio.create_subprocess_exec('/bin/true',
stdout=asyncio.subprocess.PIPE)
# ... do something else here ...
# for the sake of this test, ensure a finish here
await p.stdout.read()
p.terminate()
asyncio.run(main())
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib64/python3.8/asyncio/runners.py", line 43, in run
return loop.run_until_complete(main)
File "/usr/lib64/python3.8/asyncio/base_events.py", line 616, in run_until_complete
return future.result()
File "<stdin>", line 6, in main
File "/usr/lib64/python3.8/asyncio/subprocess.py", line 141, in terminate
self._transport.terminate()
File "/usr/lib64/python3.8/asyncio/base_subprocess.py", line 149, in terminate
self._check_proc()
File "/usr/lib64/python3.8/asyncio/base_subprocess.py", line 142, in _check_proc
raise ProcessLookupError()
ProcessLookupError
这段代码中的错误是什么?我做错了什么
我在以下版本上进行了测试:
解决方案:在调用
.terminate()
之前,使用p.returncode
检查进程是否已返回。这同样适用于调用.kill()
或.send_signal()
此代码是安全的。[*]无法在检查和
.terminate()
调用之间“收获”进程。只有在异步函数等待(await
语句)时,才能获取进程我撒谎了,这不安全。看看ThreadedChildWatcher,Unix进程可能会立即收获成果。这看起来像是一个非常恼人的比赛条件
讨论
在非异步
subprocess
模块中,调用.wait()
是获取进程并设置.returncode
的方法。如果尚未调用.wait()
,将不会设置.returncode
。如果UNIX进程退出但尚未收获,它将继续作为“僵尸”存在在
asyncio
中,事件循环获取进程并设置.returncode
。这可能发生在函数中的任何await
语句中。目前的文件没有提到这一点。获取Unix进程意味着它不再存在。没有什么可以发送信号的理论上,可以更改
asyncio
以允许问题中的代码。但是,存在向后兼容性问题。到目前为止,我怀疑一些程序依赖于.returncode
在没有.wait()
的情况下/在.wait()
之前进行设置,尽管它没有被记录在案。为了设置.returncode
,必须收获Unix进程最向后兼容的更改可能是
asyncio
自己进行检查。这对使用p.pid
调用os.kill()
的代码没有帮助。这样的代码不太可能得到支持。(首先,除非您删除或降级了FastChildWatcher,否则无法使用可移植Unix系统调用支持它)相关问题 更多 >
编程相关推荐