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

时间:2011-09-30 12:44:42

标签: python multithreading queue

我开始在队列上运行一堆线程,我想在发送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()

6 个答案:

答案 0 :(得分:5)

如果您对让其他线程正常关闭不感兴趣,只需在守护进程模式下启动它们,并将队列的连接包装在终结器线程中。

这样,您可以使用线程的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(),否则它也会忽略所有异常。但是,您可以选择任意高的数字。

答案 1 :(得分:4)

不是 Ctrl + C SIGINT

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

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

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

答案 2 :(得分:2)

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

答案 3 :(得分:0)

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

答案 4 :(得分:0)

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

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()

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

答案 5 :(得分:-1)

我设法通过清空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()