如何在协议循环中将协程打包为普通函数?

时间:2014-08-14 04:05:58

标签: python python-3.x coroutine python-asyncio

我正在使用 asyncio 作为网络框架。

在下面的代码中(low_level是我们的低级函数,main块是我们的程序条目,user_func是用户定义的函数):

import asyncio

loop = asyncio.get_event_loop()
""":type :asyncio.AbstractEventLoop"""


def low_level():
    yield from asyncio.sleep(2)


def user_func():
    yield from low_level()


if __name__ == '__main__':
    co = user_func()
    loop.run_until_complete(co)

我希望将low_level包装为普通函数而不是coroutine(对于compatibility等),但low_level在事件循环中。如何将其包装为普通函数?

1 个答案:

答案 0 :(得分:16)

由于low_level是协程,因此只能 运行asyncio事件循环。如果您希望能够从运行事件循环的不是的同步代码中调用它,则必须提供一个实际启动事件循环并运行协程直到完成的包装器: / p>

def sync_low_level():
    loop = asyncio.get_event_loop()
    loop.run_until_complete(low_level())

如果您希望能够从 部分正在运行的事件循环中调用low_level(),请将其阻塞两秒钟,但不必使用{{1答案是,你不能。事件循环是单线程的;只要执行在您的某个函数中,就会阻止事件循环。不能处理其他事件或回调。在事件循环中运行的函数将控制权交还给事件循环的唯一方法是1)yield from 2)使用return。除非您执行这两项操作,否则yield from中的asyncio.sleep来电将永远无法完成。

现在,我想你可以创建一个全新的事件循环,并使用它来作为默认事件循环的一部分运行的协程同步运行睡眠:

low_level

但我真的不确定你为什么要这样做。

如果您只是希望能够使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的方法一样,那么您可以附加回调等,只需将其包装在asyncio.async()中:

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" @asyncio.coroutine的{​​{1}}装饰器,如asyncio docs中所述:

  

协程是遵循某些约定的生成器。对于   文档目的,所有协同程序都应该用   @ asyncio.coroutine,但这不能严格执行。

修改

以下是来自同步Web框架的用户如何在不阻止其他请求的情况下调用您的应用程序:

low_level

如果Flask处理的请求调用user_func,它将阻塞直到请求完成,但是应该为@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() 中正在进行的所有异步I / O释放GIL,允许其他请求在不同的线程中处理。