TLDR:
协程必须是await
'ed 或由事件循环运行。 Acdef
功能不能await
,但它可以构造并返回一个协程。
您的实际问题是将同步代码与异步代码混合在一起。例证:
async def example():
loop.run_until_complete(py_call_foo())
这类似于将子例程放入线程中,但从不启动它。
即使启动了,这也是一个死锁:同步部分会阻止异步部分运行。
异步代码必须是await
ed
An async def
协程类似于def ...: yield
生成器:调用它只是实例化它。您必须与其交互才能实际运行它:
def foo():
print('running!')
yield 1
bar = foo() # no output!
print(next(bar)) # prints `running!` followed by `1`
同样,当你有一个async def
协程,你必须await
它或将其安排在事件循环中。自从asyncio.wait_for https://docs.python.org/3/library/asyncio-task.html#asyncio.wait_for产生一个协程,而你永远不会await
或安排它,它不会运行。这就是造成这个问题的原因RuntimeWarning
.
请注意,将协程放入的目的asyncio.wait_for
纯粹是为了添加超时。它产生一个异步包装器,它必须是await
'ed.
async def call_foo(callback):
print('call_foo')
await asyncio.wait_for(callback.foo(), timeout=2)
asyncio.get_event_loop().run_until_complete(call_foo(Callback()))
异步函数需要异步指令
异步编程的关键在于合作社:只有一个协程执行直到获得控制权。之后,另一个协程执行直到获得控制权。这意味着任何协程阻塞without让出控制也会阻止所有其他协程。
一般来说,如果某物在没有await
上下文,它是阻塞的。尤其,loop.run_until_complete
正在阻塞。您必须从同步函数调用它:
loop = asyncio.get_event_loop()
# async def function uses await
async def py_call_foo():
await call_foo(Callback())
# non-await function is not async
def example():
loop.run_until_complete(py_call_foo())
example()
从协程返回值
协程可以return
结果就像常规函数一样。
async def make_result():
await asyncio.sleep(0)
return 1
If you await
从另一个协程中,你可以直接得到返回值:
async def print_result():
result = await make_result()
print(result) # prints 1
asyncio.get_event_loop().run_until_complete(print_result())
要从常规子例程内的协程获取值,请使用run_until_complete
运行协程:
def print_result():
result = asyncio.get_event_loop().run_until_complete(make_result())
print(result)
print_result()
A cdef/cpdef
函数不能是协程
Cython 通过以下方式支持协程yield from
and await
仅适用于 Python 函数。即使对于经典的协程来说,cdef
不可能:
Error compiling Cython file:
------------------------------------------------------------
cdef call_foo(callback):
print('call_foo')
yield from asyncio.wait_for(callback.foo(), timeout=2)
^
------------------------------------------------------------
testbed.pyx:10:4: 'yield from' not supported here
你很好calling同步的cdef
来自协程的函数。你很好调度来自 a 的协程cdef
功能。
但你不能await
从里面一个cdef
函数,也不await
a cdef
功能。如果您需要这样做,如您的示例所示,请使用常规def
功能。
但是,您可以构造并返回一个协程cdef
功能。这可以让您await
外部协程的结果:
# inner coroutine
async def pingpong(what):
print('pingpong', what)
await asyncio.sleep(0)
return what
# cdef layer to instantiate and return coroutine
cdef make_pingpong():
print('make_pingpong')
return pingpong('nananana')
# outer coroutine
async def play():
for i in range(3):
result = await make_pingpong()
print(i, '=>', result)
asyncio.get_event_loop().run_until_complete(play())
请注意,尽管await
, make_pingpong
不是协程。它只是一个协程工厂。