Skip to main content
 首页 » 编程设计

python之异步 : Is it possible to release control of the event loop without using yield from

2025年05月04日15mayingbao

我想知道是否有任何方法可以在函数中释放一段时间内的 asyncio 控制循环,而无需使用协程装饰器和 yield from 关键字?

import asyncio 
import time 
 
 
class MyClass(object): 
 
    def do_something_periodically(self, delay, repeats): 
        for i in range(repeats): 
            # Do something useful 
            self._sleep(delay) 
 
    def _sleep(self, delay): 
        time.sleep(delay) 
 
 
class MyAsyncioClass(MyClass): 
 
    def _sleep(self, delay): 
        # Perform an asyncio.sleep(delay) here which yields control of the event loop 
        # and waits for time delay before returning 
 
 
loop = asyncio.get_event_loop() 
obj1 = MyAsyncioClass() 
obj2 = MyAsyncioClass() 
loop.run_until_complete(asyncio.wait( 
    [obj1.do_something_periodically(1000, 3), 
     obj2.do_something_periodically(2000, 2)])) 

我希望能够这样做,以便可以从对异步一无所知的代码调用 do_something_periodically 方法,但会在 sleep 期间释放循环控制。这可能吗?

谢谢!

编辑以显示我的特定用例的简化版本

请您参考如下方法:

这不是 asyncio 的工作方式。它使用显式异步模型——如果代码要将控制权返回给事件循环,它要么必须使用yield from,要么必须使用回调/ future 。如果您在一个函数内(例如 do_something_periodically),您不能将控制权返回到事件循环,除非 1) 使用 yield from 2) 完全退出该方法。您可以使用类的 asyncio 和非 asyncio 版本重复使用一些代码,但是任何需要调用 coroutine< 的方法,也必须是 coroutine 本身:

class MyClass(object): 
 
    def do_something_periodically(self, delay, repeats): 
        for i in range(repeats): 
            self.do_something_useful() 
            self._sleep(delay) 
 
    def _sleep(self, delay): 
        time.sleep(delay) 
 
    def do_something_useful(self): 
        # Do something useful here, which doesn't need to yield to the event loop 
 
class MyAsyncioClass(MyClass): 
 
    @asyncio.coroutine 
    def do_something_periodically(self, delay, repeats): 
        for i in range(repeats): 
            self.do_something_useful() 
            yield from self._sleep(delay) 
 
    @asyncio.coroutine 
    def _sleep(self, delay): 
        yield from asyncio.sleep(delay) 

也就是说,看起来您的特定用例可能会以另一种方式解决,但它看起来有点难看,并且需要更改 MyClass 逻辑:

class MyClass(object): 
 
    def do_something_periodically(self, delay, repeats, i=0): 
        while i < repeats: 
            # do something useful 
            if not self._sleep(delay, repeats, i): 
                break 
            i+= 1 
        return i 
 
    def _sleep(self, delay, repeats, i): 
        time.sleep(delay) 
        return True 
 
class MyAsyncioClass(MyClass): 
 
    def do_something_periodically(self, delay, repeats, i=0): 
        out = super().do_something_periodically(delay, repeats, i) 
        if out == repeats: 
            asyncio.get_event_loop().stop() 
 
    def _sleep(self, delay, repeats, i): 
        i+=1 
        asyncio.get_event_loop().call_later(delay,  
                                            self.do_something_periodically,  
                                            delay, repeats, i) 
        return False 

我们使用 loop.call_later 来执行与 asyncio.sleep 等效的操作,并调整 do_something_periodically 以支持完全迭代 code>while 循环用于正常用例,但也可以在 asyncio 情况下使用递增的 i 值重复调用。

不幸的是,没有简单、可靠的方法来为同步和 asyncio 用例重用相同的代码。这是显式异步框架(如 asyncio/tornado)与 gevent 等使用隐式异步模型的框架的主要缺点之一。使用 gevent,time.sleep(delay) 会使用 gevent 版本进行修补,该版本会将控制权交还给事件循环,直到 sleep 已完成,这意味着无需更改代码。