繁体   English   中英

Python如何用信号杀死队列中阻塞的线程?

[英]Python how to kill threads blocked on queue with signals?

我在队列上启动了一堆线程,我想在发送 SIGINT (Ctrl+C) 时杀死它们。 处理这个问题的最佳方法是什么?

targets = Queue.Queue()
threads_num = 10
threads = []

for i in threads_num:
    t = MyThread()
    t.setDaemon(True)
    threads.append(t)
    t.start()

targets.join()

如果您不想让其他线程正常关闭,只需以守​​护进程模式启动它们并将队列的加入包装在终止线程中。

这样,您可以使用线程的join方法——它支持超时并且不会阻止异常——而不必等待队列的join方法。

换句话说,做这样的事情:

term = Thread(target=someQueueVar.join)
term.daemon = True
term.start()
while (term.isAlive()):
    term.join(3600)

现在,Ctrl+C 将终止 MainThread,然后 Python 解释器硬杀死所有标记为“守护进程”的线程。 请注意,这意味着您必须为所有其他线程设置“Thread.daemon”,或者通过捕获正确的异常(KeyboardInterrupt 或 SystemExit)并执行任何需要为它们退出的操作来优雅地关闭它们。

另请注意,您绝对需要将一个数字传递给term.join() ,否则它也会忽略所有异常。 不过,您可以选择任意大的数字。

不是Ctrl + C SIGINT吗?

无论如何,您可以为适当的信号安装一个处理程序,并在处理程序中:

  • 设置一个全局标志,指示工人退出,并确保他们定期检查
  • 或将 10 个关闭令牌放在队列中,并让工作人员在弹出此魔术令牌时退出
  • 或者设置一个标志来指示主线程推送这些令牌,确保主线程检查该标志

等等。主要取决于您要中断的应用程序的结构。

一种方法是为SIGTERM安装一个直接调用os._exit(signal.SIGTERM)的信号处理程序。 但是,除非您为Queue.get指定可选的timeout参数, Queue.get信号处理函数将在get方法返回后才会运行。 (这完全没有记录;这是我自己发现的。)因此,您可以将sys.maxint指定为超时,并将Queue.get调用置于重试循环中以解决该问题。

这就是我解决这个问题的方式。

class Worker(threading.Thread):
    def __init__(self):
        self.shutdown_flag = threading.Event()
    def run(self):
        logging.info('Worker started')
        while not self.shutdown_flag.is_set():
            try:
                task = self.get_task_from_queue()
            except queue.Empty:
                continue
            self.process_task(task)

    def get_task_from_queue(self) -> Task:
        return self.task_queue.get(block=True, timeout=10)
    def shutdown(self):
        logging.info('Shutdown received')
        self.shutdown_flag.set()

收到信号后,主线程在工作线程上设置关闭事件。 工作人员在阻塞队列中等待,但每 10 秒检查一次是否收到关闭信号。

为什么不为队列上的任何操作设置超时? 然后您的线程可以通过检查是否引发事件来定期检查它们是否必须完成。

我设法通过清空KeyboardInterrupt上的队列并让线程优雅地停止自己来解决这个问题。

我不知道这是否是处理这个问题的最好方法,但它很简单而且很干净。

targets = Queue.Queue()
threads_num = 10
threads = []

for i in threads_num:
    t = MyThread()
    t.setDaemon(True)
    threads.append(t)
    t.start()

while True:
    try:
        # If the queue is empty exit loop
        if self.targets.empty() is True:
            break

    # KeyboardInterrupt handler
    except KeyboardInterrupt:
        print "[X] Interrupt! Killing threads..."
        # Substitute the old queue with a new empty one and exit loop
        targets = Queue.Queue()
        break

# Join every thread on the queue normally
targets.join()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM