Python 3 asyncio - 来自vs asyncio.async堆栈使用率

Iva*_*lev 4 python coroutine python-3.x python-asyncio

我正在使用Python 3 asyncio框架评估不同的模式以定期执行(实际的睡眠/延迟以简化),我有两段代码表现得很差,我无法解释原因.yield from正如我所料,第一个版本用于递归调用自身耗尽大约1000次迭代的堆栈.第二个版本递归调用协程,但委托实际的事件循环执行到asyncio.async并且不会耗尽堆栈.你能详细解释为什么第二个版本没有使用堆栈吗?执行此协程的两种方式有什么区别?

第一版(收益率):

@asyncio.coroutine
def call_self(self, i):
    print('calling self', i)
    yield from self.call_self(i + 1)
Run Code Online (Sandbox Code Playgroud)

第二个版本(asyncio.async):

@asyncio.coroutine
def call_self(self, i):
    print('calling self', i)
    asyncio.async(self.call_self(i + 1))
Run Code Online (Sandbox Code Playgroud)

dan*_*ano 11

第一个例子,使用yield from,实际上阻止每个实例,call_self直到它的递归调用call_self返回.这意味着调用堆栈会持续增长,直到用完堆栈空间.正如您所提到的,这是明显的行为.

第二个例子,使用asyncio.async,不会阻止任何地方.因此,每个实例call_self在运行后立即退出asyncio.async(...),这意味着堆栈不会无限增长,这意味着您不会耗尽堆栈.相反,通过将事件循环包装在事件循环的迭代中,可以执行asyncio.async调度.call_selfasyncio.Task

这是__init__for Task:

def __init__(self, coro, *, loop=None):
    assert iscoroutine(coro), repr(coro)  # Not a coroutine function!
    super().__init__(loop=loop)
    self._coro = iter(coro)  # Use the iterator just in case.
    self._fut_waiter = None
    self._must_cancel = False
    self._loop.call_soon(self._step)  # This schedules the coroutine to be run
    self.__class__._all_tasks.add(self)
Run Code Online (Sandbox Code Playgroud)

调用self._loop.call_soon(self._step)是实际使协程执行的原因.因为它以非阻塞的方式发生,所以调用堆栈call_self永远不会超出对Task构造函数的调用.然后,下一个实例call_self循环在下一次迭代时启动事件循环(在前一次call_self返回时启动,假设事件循环中没有其他任何内容正在运行),完全超出前一个call_self实例的上下文.