Python 3 asyncio - yield from vs asyncio.async stack usage

白昼怎懂夜的黑 提交于 2019-12-05 18:53:20

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.

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.

Here's the __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)

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.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!