2

我想知道是否有任何方法可以在函数中释放 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)]))

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

谢谢!

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

4

2 回答 2

6

这不是asyncio工作方式。它使用显式异步模型——如果代码要将控制权返回给事件循环,它要么必须使用yield from,要么必须使用回调/ Futures。如果你在一个函数内部(比如do_something_periodically),你不能在没有 1) 使用yield from2) 完全退出方法的情况下将控制权返回给事件循环。asyncio您可以对类的版本和非版本进行一些代码重用asyncio,但是任何需要调用 a 的方法也coroutine必须是 acoroutine本身:

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.sleepdo_something_periodicallywhileiasyncio

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

于 2014-10-21T16:57:33.337 回答
0

您可能想看看Motor library 使用的解决方案。基本上他们使用greenlets来“异步”同步代码。

于 2015-04-19T12:15:22.000 回答