[英]How can I package a coroutine as normal function in event loop?
I am using asyncio for a network framework.我正在将asyncio用于网络框架。
In below code( low_level
is our low level function, main
block is our program entry, user_func
is user-defined function):在下面的代码中(
low_level
是我们的低级函数, main
block 是我们的程序入口, 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)
I want wrap the low_level
as normal function rather than coroutine
(for compatibility
etc.), but low_level
is in event loop.我想将
low_level
包装为普通函数而不是coroutine
(为了compatibility
等),但low_level
处于事件循环中。 How can wrap it as a normal function?如何将其包装为正常功能?
Because low_level
is a coroutine, it can only be used by running an asyncio
event loop.因为
low_level
是一个协程,所以只能通过运行asyncio
事件循环来使用。 If you want to be able to call it from synchronous code that isn't running an event loop, you have to provide a wrapper that actually launches an event loop and runs the coroutine until completion:如果您希望能够从不运行事件循环的同步代码中调用它,则必须提供一个包装器来实际启动事件循环并运行协程直到完成:
def sync_low_level():
loop = asyncio.get_event_loop()
loop.run_until_complete(low_level())
If you want to be able to call low_level()
from a function that is part of the running event loop, have it block for two seconds, but not have to use yield from
, the answer is that you can't.如果你希望能够调用
low_level()
从一个函数,运行事件循环的一部分,有它阻止两秒钟,但不是必须使用的yield from
,答案是,你不能。 The event loop is single-threaded;事件循环是单线程的; whenever execution is inside one of your functions, the event loop is blocked.
每当执行在您的函数之一内时,事件循环就会被阻止。 No other events or callbacks can be processed.
不能处理其他事件或回调。 The only ways for a function running in the event loop to give control back to the event loop are to 1)
return
2) use yield from
.在事件循环中运行的函数将控制权交还给事件循环的唯一方法是 1)
return
2) 使用yield from
。 The asyncio.sleep
call in low_level
will never be able to complete unless you do one those two things.除非你做这两件事之一,否则
low_level
的asyncio.sleep
调用将永远无法完成。
Now, I suppose you could create an entirely new event loop , and use that to run the sleep synchronously from a coroutine running as part of the default event loop:现在,我想您可以创建一个全新的事件循环,并使用它从作为默认事件循环的一部分运行的协程同步运行睡眠:
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())
But I'm really not sure why you'd want to do that.但我真的不确定你为什么要这样做。
If you just want to be able to make low_level
act like a method returning a Future
, so you can attach callbacks, etc. to it, just wrap it in asyncio.async()
:如果你只是想让
low_level
得像一个返回Future
的方法,所以你可以给它附加回调等,只需将它包装在asyncio.async()
:
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()
Output:输出:
<2 second delay>
"Done sleeping"
Also, in your example code, you should use the @asyncio.coroutine
decorator for both low_level
and user_func
, as stated in the asyncio
docs :此外,在您的示例代码中,您应该对
low_level
和user_func
使用@asyncio.coroutine
装饰器,如asyncio
文档中所述:
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.
出于文档目的,所有协程都应使用@asyncio.coroutine 进行修饰,但这不能严格执行。
Edit:编辑:
Here's how a user from a synchronous web framework could call into your application without blocking other requests:以下是来自同步 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()
If a request being handled by Flask calls thr_low_level
, it will block until the request is done, but the GIL should be released for all of the asynchronous I/O going on in low_level
, allowing other requests to be handled in separate threads.如果 Flask 处理的请求调用
thr_low_level
,它将阻塞,直到请求完成,但应该为low_level
进行的所有异步 I/O 释放 GIL,允许在单独的线程中处理其他请求。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.