我有一个带有一堆asyncio.coroutine
的守护程序,可以归结为类似的东西
import asyncio
import signal
class Daemon:
def __init__(self, loop=asyncio.get_event_loop()):
self.loop = loop
self.running = False
self.tasks = {
'coroutine1': asyncio.ensure_future(self.coroutine1()),
'coroutine2': asyncio.ensure_future(self.coroutine2()),
}
def run(self):
self.running = True
for task in self.tasks.values():
task.add_done_callback(self.task_done_callback)
# gracefuly close everything when SIGINT (could be ^C) is received
self.loop.add_signal_handler(signal.SIGINT, self.close)
self.loop.run_forever()
def close(self):
self.running = False
self.loop.run_until_complete(self.tasks['coroutine1'])
self.loop.run_until_complete(self.tasks['coroutine2'])
def task_done_callback(self, future):
for task in self.tasks.values():
if not task.done():
return
self.loop.stop()
@asyncio.coroutine
def coroutine1(self):
while self.running:
print('coroutine1: do stuff')
yield from asyncio.sleep(1)
@asyncio.coroutine
def coroutine2(self):
while self.running:
print('coroutine2: do some other stuff')
yield from asyncio.sleep(3)
if __name__ == '__main__':
loop = asyncio.get_event_loop()
daemon = Daemon(loop)
daemon.run()
loop.close()
当程序收到SIGINT
时,守护程序会正常关闭。发生这种情况时,会触发close()
方法,该方法负责通知所有正在运行的任务,他们应该完成工作并停止。只需将running
设置为False
即可完成此操作。每当任务完成时,都会触发Daemon.task_done_callback
。它会检查是否所有任务都已完成,如果是这样,那么它就会停止循环。
这里的问题是close()
方法不起作用。这是因为我在循环播放时通过loop.run_until_complete
调用run_forever
。这产生RuntimeError('This event loop is already running')
。
重要的是: coroutine1
需要在coroutine2
之前完成,因为coroutine1
如果coroutine2
不再执行其操作,可能会遇到问题。< /强>
我的问题是如何确保coroutine1
之前完成coroutine2
?
答案 0 :(得分:0)
这是实现这一目标的一种方式。 (我删除了signal
部分,只限制了coroutine1
的运行时间。
import asyncio
class Daemon:
def __init__(self, loop=asyncio.get_event_loop()):
self.loop = loop
self.tasks = {
'coroutine1': asyncio.ensure_future(self.coroutine1()),
'coroutine2': asyncio.ensure_future(self.coroutine2())}
def run(self):
for task in self.tasks.values():
task.add_done_callback(self.task_done_callback)
self.loop.run_forever()
def task_done_callback(self, future):
if all(task.done() for task in self.tasks.values()):
self.loop.stop()
@asyncio.coroutine
def coroutine1(self):
for _ in range(5):
print('coroutine1: doing stuff')
yield from asyncio.sleep(0.2)
print('coroutine1: done!')
@asyncio.coroutine
def coroutine2(self):
while not self.tasks['coroutine1'].done():
print('coroutine2: doing stuff while coro1 is running')
yield from asyncio.sleep(0.2)
print('coroutine2: doing stuff after coro1 has ended')
yield from asyncio.sleep(1)
print('coroutine2: done!')
if __name__ == '__main__':
loop = asyncio.get_event_loop()
daemon = Daemon(loop)
daemon.run()
这里最重要的是检查coroutine1
是否仍在运行(可以仅查询您的self.tasks
属性。)
现在要将其与signal
集成以停止coroutine1
我建议您注册一个设置标记的简单函数(例如self.signal_flag
)。然后使用类似coroutine1
的内容在while not self.signal_flag: ...
内循环。这些是我建议的完整解决方案的变化:
class Daemon:
def __init__(self, loop=asyncio.get_event_loop()):
...
self.signal_flag = False
def run(self):
...
self.loop.add_signal_handler(signal.SIGINT, self.set_signal_flag)
self.loop.run_forever()
def set_signal_flag(self):
print('caught signal!')
self.signal_flag = True
async def coroutine1(self):
while not self.signal_flag:
print('coroutine1: doing stuff')
await asyncio.sleep(0.2)
print('coroutine1: done!')
还要注意,不需要close
循环。在解释器继续运行的IDE中,这将使事情变得比它们需要的更复杂......
从python 3.5开始,你可以(并且可能应该)将这种语法用于你的协同程序:
async def coroutine1(self):
for _ in range(5):
print('coroutine1: doing stuff')
await asyncio.sleep(0.2)
print('coroutine1: done!')