2017-03-06 20 views
2

以下最小程序会重现此问题。通过SIGTERM间接停止python asyncio事件循环没有任何效果

import asyncio 
import signal 

class A: 
    def __init__(self): 
     self._event_loop = asyncio.new_event_loop() 

    def run(self): 
     print('starting event loop') 
     self._event_loop.run_forever() 
     print('event loop has stopped') 

    def stop(self): 
     print('stopping event loop') 
     self._event_loop.stop() 


if __name__ == '__main__': 
    a = A() 

    def handle_term(*args): 
     a.stop() 

    signal.signal(signal.SIGTERM, handle_term) 
    a.run() 

如果你运行该程序,并发送一个SIGTERM的过程,在第16行的打印语句(停止事件循环)被调用,但PROGRAMM不会终止,并在第13行的打印语句(事件循环有停止)从未被调用。所以看起来,事件循环从未停止,并且无限期地阻止了self._event_loop.run_forever()

这是为什么?

注意:该程序的修改版本,其中a.stop()未由信号处理程序调用,而是由具有延迟的单独线程调用,如预期的那样工作。如何调用a.stop()

回答

2

代替signal.signal()使用loop.add_signal_handler()

import asyncio 
import signal 

import os 


class A: 
    def __init__(self): 
     self.loop = asyncio.new_event_loop() 
     self.loop.add_signal_handler(signal.SIGTERM, self.stop) 

    def stop(self): 
     print('stopping') 
     self.loop.stop() 

    def run(self, close=True): 
     print('starting loop') 
     try: 
      self.loop.run_forever() 
      print('loop stopped') 
     finally: 
      if close: 
       self.loop.close() 


if __name__ == '__main__': 
    print("to stop run:\nkill -TERM {}".format(os.getpid())) 
    a = A() 
    a.run() 
+0

这就是我如何解决它现在。但我不喜欢它。它将在A类的事件循环内运行的逻辑与如何停止的逻辑相耦合。从结构上来说,将这两者分离得更干净,并让A的事件循环从A之外停止。 另外:我仍然很好奇为什么我的示例不起作用。据我所知,它应该工作,对吧? –

+0

您可以使用'faulthandler.dump_traceback()'来查看您的程序卡在'select()'中。 Unix需要使用['signal.set_wakeup_fd()'](https://docs.python.org/3.4/library/signal.html#signal.set_wakeup_fd)唤醒poll/select。 – Udi