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

时间:2014-10-24 06:36:50

标签: python python-3.x coroutine 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)

第二个版本(asyncio.async):

@asyncio.coroutine
def call_self(self, i):
    print('calling self', i)
    asyncio.async(self.call_self(i + 1))

1 个答案:

答案 0 :(得分:11)

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

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

以下是__init__的{​​{1}}:

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) 的调用实际上是使协程执行的。因为它以非阻塞方式发生,所以来自self._loop.call_soon(self._step)的调用堆栈永远不会超出对call_self构造函数的调用。然后,Task的下一个实例在下一次迭代时被事件循环启动(一旦上一次call_self返回就会启动,假设在事件循环中没有其他任何东西运行),完全在上一个call_self实例的上下文。