想象一下以下代码:
import asyncio
loop = asyncio.get_event_loop()
@asyncio.coroutine
def coro():
yield from asyncio.sleep(1)
def done_callback(future):
print("Callback called")
@asyncio.coroutine
def run():
future = asyncio.async(coro(), loop=loop)
future.add_done_callback(done_callback)
yield from asyncio.wait([future])
print("Wait returned")
loop.run_until_complete(run())
输出是:
$ python3 /tmp/d.py
Callback called
Wait returned
因此在done_callback
返回之前调用了wait
。
这是保证的行为吗?我在文档中没有找到任何关于此的内容。
在done_callback
返回后调用wait
时可能出现这种情况吗?
答案 0 :(得分:4)
使用当前的asyncio
实现,只要在add_done_callback
实际完成的事件循环迭代之前调用coro
,所有使用add_done_callback
调度的回调都将在之前执行wait
取消阻止。原因是asyncio.wait
在您传递给它的所有add_done_callback
个实例上内部调用Future
,因此它只是Task
的回调链中的另一个回调。当您Task
完成后,asyncio
就会调用set_result
,如下所示:
def set_result(self, result):
"""Mark the future done and set its result.
If the future is already done when this method is called, raises
InvalidStateError.
"""
if self._state != _PENDING:
raise InvalidStateError('{}: {!r}'.format(self._state, self))
self._result = result
self._state = _FINISHED
self._schedule_callbacks()
_schedule_callbacks
看起来像这样:
def _schedule_callbacks(self):
"""Internal: Ask the event loop to call all callbacks.
The callbacks are scheduled to be called as soon as possible. Also
clears the callback list.
"""
callbacks = self._callbacks[:]
if not callbacks:
return
self._callbacks[:] = []
for callback in callbacks:
self._loop.call_soon(callback, self)
因此,Task
完成后,loop.call_soon
用于安排所有回调(包括done_callback
功能,以及asyncio.wait
添加的回调)。
事件循环将在一次迭代中处理所有内部回调列表中的回调,这意味着asyncio.wait
回调和done_callback
将一起执行单事件循环迭代:
# This is the only place where callbacks are actually *called*.
# All other places just add them to ready.
# Note: We run all currently scheduled callbacks, but not any
# callbacks scheduled by callbacks run this time around --
# they will be run the next time (after another I/O poll).
# Use an idiom that is thread-safe without using locks.
ntodo = len(self._ready)
for i in range(ntodo):
handle = self._ready.popleft()
if handle._cancelled:
continue
if self._debug:
t0 = self.time()
handle._run()
因此,只要您的add_done_callback
在完成coro
的事件循环迭代之前运行,就可以保证(至少在当前实现中)它将在{{1}之前运行解禁。但是,如果在asyncio.wait
完成后执行add_done_callback
,或者在coro
完成的同一事件循环迭代中执行coro
,则在asyncio.wait
完成之后才会运行。
我想说如果在add_done_callback
之前调用asyncio.wait
,就像在您的示例中一样,您可以确信它将始终在wait
取消阻止之前运行,因为您的回调将超前回调链中的asyncio.wait
回调。如果您在 add_done_callback
启动后最终调用asyncio.wait
,它现在仍然可以正常工作,但从理论上讲,实现可能会以一种不会改变的方式发生变化。例如,可以将其更改为仅在每个事件循环迭代中处理有限数量的回调。我怀疑是否会做出改变,但这是可能的。