通过 SIGTERM 间接停止 python asyncio 事件循环无效
Indirectly stopping a python asyncio event loop through SIGTERM has no effect
以下最小程序重现了该问题。
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 行中的打印语句(停止事件循环)但程序不会终止,并且第 13 行中的打印语句(事件循环已停止)永远不会被调用。所以似乎事件循环永远不会停止并且 self._event_loop.run_forever()
无限期地阻塞。
这是为什么?
注意:程序的修改版本,其中 a.stop()
不是由信号处理程序调用,而是由具有延迟的单独线程调用,按预期工作。 a.stop()
的称呼方式有何不同?
而不是 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()
以下最小程序重现了该问题。
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 行中的打印语句(停止事件循环)但程序不会终止,并且第 13 行中的打印语句(事件循环已停止)永远不会被调用。所以似乎事件循环永远不会停止并且 self._event_loop.run_forever()
无限期地阻塞。
这是为什么?
注意:程序的修改版本,其中 a.stop()
不是由信号处理程序调用,而是由具有延迟的单独线程调用,按预期工作。 a.stop()
的称呼方式有何不同?
而不是 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()