asyncio.wait是否仅在调用了所有done_callbacks后返回?

时间:2015-04-26 21:05:20

标签: python python-3.x python-asyncio

想象一下以下代码:

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时可能出现这种情况吗?

1 个答案:

答案 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 ,它现在仍然可以正常工作,但从理论上讲,实现可能会以一种不会改变的方式发生变化。例如,可以将其更改为仅在每个事件循环迭代中处理有限数量的回调。我怀疑是否会做出改变,但这是可能的。