Python 3 asyncio-vs asyncio的收益 [英] Python 3 asyncio - yield from vs asyncio.async stack usage

查看:100
本文介绍了Python 3 asyncio-vs asyncio的收益的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用Python 3 asyncio框架评估定期执行的不同模式(为简洁起见,省略了实际的睡眠/延迟),并且我有两段代码表现不同,我无法解释原因。如我所料,第一个版本使用 yield from 来调用自身,从而在大约1000次迭代中递归耗尽了堆栈。第二个版本递归调用协程,但是将实际的事件循环执行委托给 asyncio.async ,并且不会耗尽堆栈。您能否详细说明第二个版本为什么不使用该堆栈?执行此协程的两种方式之间有什么区别?

I'm evaluating different patterns for periodic execution (actual sleep/delays ommited for brevity) using the Python 3 asyncio framework, and I have two pieces of code that behave diffrently and I can't explain why. The first version, which uses yield from to call itself recursively exhausts the stack in about 1000 iterations, as I expected. The second version calls the coroutine recursively, but delegates actual event loop execution to asyncio.async and doesn't exhaust the stack. Can you explain in detail why the stack isn't being used by the second version? What are the differences between the two ways of executing this coroutine?

第一个版本(从中产生):

First version (yield from):

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

第二版(asyncio.async):

Second version (asyncio.async):

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


推荐答案

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

The first example, using yield from, actually blocks each instance of call_self until its recursive call to call_self returns. This means the call stack keeps growing until you run out of stack space. As you mentioned, this is the obvious behavior.

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

The second example, using asyncio.async, doesn't block anywhere. So, each instance of call_self immediately exits after running asyncio.async(...), which means the stack doesn't grow infinitely, which means you don't exhaust the stack. Instead, asyncio.async schedules call_self gets to be executed on the iteration of the event loop, by wrapping it in a asyncio.Task.

这是 __ init __ 任务

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 假定事件循环中没有其他任何运行,则返回),完全在先前 call_self 实例的上下文之外。

The call to self._loop.call_soon(self._step) is what actually makes the coroutine execute. Because it's happening in a non-blocking way, the call stack from call_self never grows beyond the call to the Task constructor. Then the next instance of call_self gets kicked off by the event loop on its next iteration (which starts as soon as the previous call_self returns, assuming nothing else is running in the event loop), completely outside of the context of the previous call_self instance.

这篇关于Python 3 asyncio-vs asyncio的收益的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆