我想知道是否有任何方法可以在函数中释放一段时间内的 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 已完成,这意味着无需更改代码。
