因为low_level是一个协程,所以它只能通过运行asyncio事件循环来使用。如果希望能够从运行事件循环的同步代码中调用它,则必须提供一个包装器,该包装器实际启动一个事件循环并运行协程,直到完成:def sync_low_level():
loop = asyncio.get_event_loop()
loop.run_until_complete(low_level())
如果您想从一个函数调用low_level(),该函数是运行事件循环的一部分,让它阻塞两秒钟,但不必使用yield from,答案是您不能这样做。事件循环是单线程的;当执行在一个函数内时,事件循环就会被阻塞。无法处理其他事件或回调。在事件循环中运行的函数将控制权交还给事件循环的唯一方法是1)return2)使用yield from。除非您做这两件事中的一件,否则asyncio.sleep调用low_level将永远无法完成。
现在,我想您可以创建一个全新的事件循环,并使用它从作为默认事件循环一部分运行的协同程序同步运行sleep:import asyncio
loop = asyncio.get_event_loop()
@asyncio.coroutine
def low_level(loop=None):
yield from asyncio.sleep(2, loop=loop)
def sync_low_level():
new_loop = asyncio.new_event_loop()
new_loop.run_until_complete(low_level(loop=new_loop))
@asyncio.coroutine
def user_func():
sync_low_level()
if __name__ == "__main__":
loop.run_until_complete(user_func())
但我真的不知道你为什么要这么做。
如果您只想让low_level像返回Future的方法一样,这样就可以将回调等附加到它,只需将它包装成^{}:loop = asyncio.get_event_loop()
def sleep_done(fut):
print("Done sleeping")
loop.stop()
@asyncio.coroutine
def low_level(loop=None):
yield from asyncio.sleep(2, loop=loop)
def user_func():
fut = asyncio.async(low_level())
fut.add_done_callback(sleep_done)
if __name__ == "__main__":
loop.call_soon(user_func)
loop.run_forever()
输出:<2 second delay>
"Done sleeping"
另外,在示例代码中,应该对low_level和user_func同时使用@asyncio.coroutine装饰符,如^{} docs中所述:A coroutine is a generator that follows certain conventions. For
documentation purposes, all coroutines should be decorated with
@asyncio.coroutine, but this cannot be strictly enforced.
编辑:
以下是来自同步web框架的用户如何在不阻止其他请求的情况下调用应用程序:@asyncio.coroutine
def low_level(loop=None):
yield from asyncio.sleep(2, loop=loop)
def thr_low_level():
loop = asyncio.new_event_loop()
t = threading.Thread(target=loop.run_until_complete, args(low_level(loop=loop),))
t.start()
t.join()
如果一个由Flask处理的请求调用thr_low_level,它将阻塞直到请求完成,但是应该为low_level中进行的所有异步I/O释放GIL,从而允许在单独的线程中处理其他请求。