我有一个假设,如果我用 asyncio 编写相互递归协程,它们不会遇到最大递归深度异常,因为事件循环正在调用它们(并且像蹦床一样运行)。然而,当我这样写时,情况并非如此:
import asyncio
@asyncio.coroutine
def a(n):
print("A: {}".format(n))
if n > 1000: return n
else: yield from b(n+1)
@asyncio.coroutine
def b(n):
print("B: {}".format(n))
yield from a(n+1)
loop = asyncio.get_event_loop()
loop.run_until_complete(a(0))
当这个运行时,我得到RuntimeError: maximum recursion depth exceeded while calling a Python object
.
有没有办法阻止堆栈在带有 asyncio 的递归协程中增长?
为了防止堆栈增长,您必须允许每个协程在安排下一个递归调用后实际退出,这意味着您必须避免使用yield from
。相反,你使用asyncio.async (or asyncio.ensure_future
如果使用 Python 3.4.4+) 通过事件循环安排下一个协程,并使用Future.add_done_callback安排回调在递归调用返回后运行。然后每个协程返回一个asyncio.Future
对象,其结果集位于回调中,该回调在其计划的递归调用完成时运行。
如果您实际看到代码,可能最容易理解:
import asyncio
@asyncio.coroutine
def a(n):
fut = asyncio.Future() # We're going to return this right away to our caller
def set_result(out): # This gets called when the next recursive call completes
fut.set_result(out.result()) # Pull the result from the inner call and return it up the stack.
print("A: {}".format(n))
if n > 1000:
return n
else:
in_fut = asyncio.async(b(n+1)) # This returns an asyncio.Task
in_fut.add_done_callback(set_result) # schedule set_result when the Task is done.
return fut
@asyncio.coroutine
def b(n):
fut = asyncio.Future()
def set_result(out):
fut.set_result(out.result())
print("B: {}".format(n))
in_fut = asyncio.async(a(n+1))
in_fut.add_done_callback(set_result)
return fut
loop = asyncio.get_event_loop()
print("Out is {}".format(loop.run_until_complete(a(0))))
Output:
A: 0
B: 1
A: 2
B: 3
A: 4
B: 5
...
A: 994
B: 995
A: 996
B: 997
A: 998
B: 999
A: 1000
B: 1001
A: 1002
Out is 1002
现在,您的示例代码实际上并未返回n
一直备份堆栈,因此您可以制作功能相同但更简单的东西:
import asyncio
@asyncio.coroutine
def a(n):
print("A: {}".format(n))
if n > 1000: loop.stop(); return n
else: asyncio.async(b(n+1))
@asyncio.coroutine
def b(n):
print("B: {}".format(n))
asyncio.async(a(n+1))
loop = asyncio.get_event_loop()
asyncio.async(a(0))
loop.run_forever()
但我怀疑你真的想回来n
一路备份。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)