简体   繁体   English

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

[英]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_levelasyncio.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_leveluser_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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM