asyncio:是否可以在不使用yield的情况下释放对事件循环的控制?

时间:2014-10-20 12:56:51

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

我想知道是否有办法在函数中释放asyncio控件循环一段时间而不必使用coroutine装饰器和关键字的收益?

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)]))

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

谢谢!

已编辑以显示我的特定用例的缩减版

2 个答案:

答案 0 :(得分:6)

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

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循环,但是也可以while案例中i的值越来越多地重复调用。

不幸的是,对于同步和asyncio用例重用相同的代码并不容易,可靠。它是显式异步框架(如asyncio / asynciotornado之类的主要缺点之一,它使用隐式异步模型。使用gevent时,gevent会使用time.sleep(delay)版本修补,该版本会将控制权交还给事件循环,直到gevent完成,这意味着不需要更改代码。< / p>

答案 1 :(得分:0)

您可能需要查看at the solution that is used by the Motor library。基本上他们使用greenlets来“异步”同步代码。