问题描述
import asyncio
def add_world_task():
loop = asyncio.get_running_loop()
loop.call_soon(print_world)
# asyncio.create_task(print_world()) # <-- This is a "fix",async def print_hello():
print("hello!")
add_world_task()
async def print_world():
print("world!")
loop = asyncio.new_event_loop()
loop.run_until_complete(print_hello())
以下代码将运行,并警告未运行print_world
协程:
hello!
/usr/lib/python3.7/asyncio/events.py:88: RuntimeWarning: coroutine 'print_world' was never awaited
self._context.run(self._callback,*self._args)
RuntimeWarning: Enable tracemalloc to get the object allocation traceback
Process finished with exit code 0
这对我来说很有意义,根据call_soon
的文档,放置在call_soon
中的回调将在事件循环的下一次迭代中运行。 run_until_complete
运行直到方法完成。因此,print_hello
完成后就不会再有事件循环的迭代了,协程print_world
也不会运行。
我不明白的是为什么asyncio.create_task(print_world())
在给定run_until_complete
的情况下成功运行。 print_hello
完成后,协程print_world
似乎仍然可以根据run_until_complete
的文档在事件循环中运行?
这是由于call_soon
将协程放置在事件循环的开始,create_task
放置在事件循环的后面-和run_until_complete
实际完成了当前任务和事件循环迭代的其余部分?
(您可能会发现我直接使用同步add_world_task
而不是直接使用await print_world
很奇怪。不幸的是,这很像我的实际情况。我有一个同步方法(Django信号方法),它需要运行一个异步方法,而一个事件循环可能已经在运行。可以通过在正在运行的事件循环中添加协程来做到这一点
解决方法
call_soon
和create_task
都将回调安排在当前迭代的末尾。两者都回避执行回调,因为在fully completes each iteration之前的事件循环examining whether to exit是由于调用了loop.stop()
(这就是run_until_complete()
是implemented的原因)。 / p>
call_soon
在您的情况下无法工作,原因完全不同:它旨在运行非异步回调,即它仅调用的普通函数。您向它传递了一个协程(异步)函数,该函数也是一个有效的可调用函数,但是仅调用该可调用函数不会做任何事情,它只是创建一个协程对象,您应该等待该对象或将其传递给create_task
。由于call_soon
希望回调具有副作用,因此它将该协程对象放在地板上,这导致该对象永远不会执行并显示警告。
安排异步功能尽快执行的正确方法就是通过传递其结果asyncio.create_task
来修复它的方法。