为什么 await 不等待 asyncio.create_subprocess_exec()
Why await doesn't wait asyncio.create_subprocess_exec()
我正在编写一个协程来根据教程在 python 中执行 shell 命令。这是基本的:
import asyncio
async def async_procedure():
process = await asyncio.create_subprocess_exec('ping', '-c', '2', 'google.com')
await process.wait()
print('async procedure done.')
loop = asyncio.get_event_loop()
loop.run_until_complete(async_procedure())
loop.close()
上面这段代码工作得很好。它给出了这样的结果:
PING google.com (...) 56(84) bytes of data.
64 bytes from ...: icmp_seq=1 ttl=46 time=34.8 ms
64 bytes from ...: icmp_seq=2 ttl=46 time=34.5 ms
--- google.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 33.771/34.437/34.881/0.407 ms
Process done!
当我尝试删除 process.wait():
async def async_procedure():
await asyncio.create_subprocess_exec('ping', '-c', '2', 'google.com')
print('async procedure done.')
脚本未按预期运行:
Process done! # This line should be lastest line
PING google.com (...) 56(84) bytes of data.
64 bytes from ...: icmp_seq=1 ttl=46 time=21.1 ms
64 bytes from ...: icmp_seq=2 ttl=46 time=21.8 ms
--- google.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 21.135/21.469/21.803/0.334 ms
但是在一个非常相似的例子中没有问题:
async def async_procedure():
await asyncio.sleep(2)
print('async procedure done')
- 那么为什么 await 不等待 asyncio.create_subprocess_exec() ?
文档 (https://docs.python.org/3/library/asyncio-task.html#coroutine) 说:
result = await future or result = yield from future – suspends the coroutine until the future is done, then returns the future’s result, or raises an exception, which will be propagated. (If the future is cancelled, it will raise a CancelledError exception.) Note that tasks are futures, and everything said about futures also applies to tasks.
result = await coroutine or result = yield from coroutine – wait for another coroutine to produce a result (or raise an exception, which will be propagated). The coroutine expression must be a call to another coroutine.
return expression – produce a result to the coroutine that is waiting for this one using await or yield from.
raise exception – raise an exception in the coroutine that is waiting for this one using await or yield from.
- 当协程 suspending 和 waiting 时,进程的实际流程是什么?
这里是asyncio.create_subprocess_exec()的源代码,asyncio.sleep()是协程。它们都是协程:
@coroutine
def create_subprocess_exec(program, *args, stdin=None, stdout=None,
stderr=None, loop=None,
limit=streams._DEFAULT_LIMIT, **kwds):
if loop is None:
loop = events.get_event_loop()
protocol_factory = lambda: SubprocessStreamProtocol(limit=limit,
loop=loop)
transport, protocol = yield from loop.subprocess_exec(
protocol_factory,
program, *args,
stdin=stdin, stdout=stdout,
stderr=stderr, **kwds)
return Process(transport, protocol, loop)
@coroutine
def sleep(delay, result=None, *, loop=None):
"""Coroutine that completes after a given time (in seconds)."""
if delay == 0:
yield
return result
if loop is None:
loop = events.get_event_loop()
future = loop.create_future()
h = future._loop.call_later(delay,
futures._set_result_unless_cancelled,
future, result)
try:
return (yield from future)
finally:
h.cancel()
您正在等待进程启动。你没有等它完成。 await process.wait()
等待它完成。
我正在编写一个协程来根据教程在 python 中执行 shell 命令。这是基本的:
import asyncio
async def async_procedure():
process = await asyncio.create_subprocess_exec('ping', '-c', '2', 'google.com')
await process.wait()
print('async procedure done.')
loop = asyncio.get_event_loop()
loop.run_until_complete(async_procedure())
loop.close()
上面这段代码工作得很好。它给出了这样的结果:
PING google.com (...) 56(84) bytes of data.
64 bytes from ...: icmp_seq=1 ttl=46 time=34.8 ms
64 bytes from ...: icmp_seq=2 ttl=46 time=34.5 ms
--- google.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 3004ms
rtt min/avg/max/mdev = 33.771/34.437/34.881/0.407 ms
Process done!
当我尝试删除 process.wait():
async def async_procedure():
await asyncio.create_subprocess_exec('ping', '-c', '2', 'google.com')
print('async procedure done.')
脚本未按预期运行:
Process done! # This line should be lastest line
PING google.com (...) 56(84) bytes of data.
64 bytes from ...: icmp_seq=1 ttl=46 time=21.1 ms
64 bytes from ...: icmp_seq=2 ttl=46 time=21.8 ms
--- google.com ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 21.135/21.469/21.803/0.334 ms
但是在一个非常相似的例子中没有问题:
async def async_procedure():
await asyncio.sleep(2)
print('async procedure done')
- 那么为什么 await 不等待 asyncio.create_subprocess_exec() ?
文档 (https://docs.python.org/3/library/asyncio-task.html#coroutine) 说:
result = await future or result = yield from future – suspends the coroutine until the future is done, then returns the future’s result, or raises an exception, which will be propagated. (If the future is cancelled, it will raise a CancelledError exception.) Note that tasks are futures, and everything said about futures also applies to tasks.
result = await coroutine or result = yield from coroutine – wait for another coroutine to produce a result (or raise an exception, which will be propagated). The coroutine expression must be a call to another coroutine.
return expression – produce a result to the coroutine that is waiting for this one using await or yield from.
raise exception – raise an exception in the coroutine that is waiting for this one using await or yield from.
- 当协程 suspending 和 waiting 时,进程的实际流程是什么?
这里是asyncio.create_subprocess_exec()的源代码,asyncio.sleep()是协程。它们都是协程:
@coroutine
def create_subprocess_exec(program, *args, stdin=None, stdout=None,
stderr=None, loop=None,
limit=streams._DEFAULT_LIMIT, **kwds):
if loop is None:
loop = events.get_event_loop()
protocol_factory = lambda: SubprocessStreamProtocol(limit=limit,
loop=loop)
transport, protocol = yield from loop.subprocess_exec(
protocol_factory,
program, *args,
stdin=stdin, stdout=stdout,
stderr=stderr, **kwds)
return Process(transport, protocol, loop)
@coroutine
def sleep(delay, result=None, *, loop=None):
"""Coroutine that completes after a given time (in seconds)."""
if delay == 0:
yield
return result
if loop is None:
loop = events.get_event_loop()
future = loop.create_future()
h = future._loop.call_later(delay,
futures._set_result_unless_cancelled,
future, result)
try:
return (yield from future)
finally:
h.cancel()
您正在等待进程启动。你没有等它完成。 await process.wait()
等待它完成。