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

Python 如何在事件循环中将协程打包为普通函数?,python,python-3.x,coroutine,python-asyncio,Python,Python 3.x,Coroutine,Python Asyncio,我将asyncio用于网络框架 在下面的代码中(low_-level是我们的低级函数,main块是我们的程序条目,user_-func是用户定义的函数): 我希望将低级别包装为正常功能,而不是协同程序(用于兼容性等),但低级别处于事件循环中。如何将其包装为普通函数?因为低级别是一个协同程序,所以只能通过运行异步事件循环来使用它。如果希望能够从未运行事件循环的同步代码调用它,则必须提供一个包装器,该包装器实际启动事件循环并运行协同路由,直到完成: def sync_low_level():

我将asyncio用于网络框架

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


我希望将
低级别
包装为正常功能,而不是
协同程序
(用于
兼容性
等),但
低级别
处于事件循环中。如何将其包装为普通函数?

因为
低级别
是一个协同程序,所以只能通过运行
异步
事件循环来使用它。如果希望能够从未运行事件循环的同步代码调用它,则必须提供一个包装器,该包装器实际启动事件循环并运行协同路由,直到完成:

def sync_low_level():
    loop = asyncio.get_event_loop()
    loop.run_until_complete(low_level())
如果您希望能够从作为运行事件循环一部分的函数调用
low_level()
,请将其阻塞两秒钟,但不必使用
yield from
,答案是您不能。事件循环是单线程的;每当执行在一个函数内时,事件循环就会被阻塞。无法处理其他事件或回调。在事件循环中运行的函数将控制权交还给事件循环的唯一方法是1)
return
2)使用
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())
但我真的不知道你为什么要这么做

如果您只想让
低级别
像一个返回
未来
的方法一样工作,这样您就可以将回调等附加到它,只需将它包装成:

输出:

<2 second delay>
"Done sleeping"

如果Flask处理的请求调用
thr_low_level
,它将阻塞直到请求完成,但是对于
low_level
中进行的所有异步I/O,应该释放GIL,允许在单独的线程中处理其他请求。

将其作为正常函数运行是什么意思?你想从没有作为事件循环的一部分运行的代码中调用它吗?@dano,如我所说“我编写web框架,框架运行事件循环,用户函数调用web框架提供的低级函数。考虑到与其他框架的兼容性,用户函数可以调用低级别函数作为正常函数而不是协同程序。是否无法保持与其他框架的兼容性?“请参阅更一般的内容,如果我编写web框架,框架将运行事件循环,用户函数调用web框架提供的低级函数。考虑到与其他框架的兼容性,用户函数可以调用低级别函数作为正常函数而不是协同程序。是否无法保持与其他框架的兼容性?@RobertLu所以您想知道如何从具有自己事件循环(例如)的web框架中调用asyncio协程?您对某个特定的框架感兴趣吗?@RobertLu您能分享一下哪个web框架吗?不幸的是,我不认为这是一个一刀切的(-web框架)答案。让我们来谈谈flask。在flask用户调用
connect
函数中,但我想用一个协程来替换它(当这个请求等待查询结果时,其他请求可以通过asyncio的事件循环获得CPU),我希望用户的遗留代码可以在asyncio上正常工作。@RobertLu flask实现,这意味着它是一个单线程、同步的web框架。它不是构建在事件循环上的,因此不能与构建在事件循环上的异步框架一起使用。与flask的并发性是通过将其放在支持使用线程/greenlet/pre-fork Worker来处理每个请求的Web服务器后面来实现的,而不是通过异步I/O。有关flask部署选项的列表,请参阅。
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
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()