簡體   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