Python - 多线程 - 线程在一种情况下终止。在另一个他们没有。为什么?

时间:2017-03-27 19:20:04

标签: python multithreading python-3.x queue

考虑以下我一直在学习多线程的例子。它只是Python 3.5 queue documentation的扩展示例。

它会在4个线程上打印一些数字,在队列中产生一个错误,重试此元素,如果发生KeyboardInterrupt异常,则应打印剩余的队列。

import threading
import queue
import time
import random
import traceback


def worker(q, active):
    while True:
        worker_item = q.get()
        #if worker_item == None:
        if not active.is_set():
            break
        time.sleep(random.random())
        with threading.Lock():
            if worker_item == 5 or worker_item == '5':
                try:
                    print(threading.current_thread().name + ': ' + worker_item + ' | remaining queue: ' + str(list(q.queue)))
                except TypeError:
                    print(threading.current_thread().name + ': ')
                    print(traceback.format_exc())
                    q.put(str(worker_item))
            else:
                print(threading.current_thread().name + ': ' + str(worker_item) + ' | remaining queue: ' + str(list(q.queue)))
        q.task_done()


def main():

    # INITIALIZE
    num_threads = 4
    stack1 = list(range(1, 21))
    stack2 = list(range(101, 121))
    q = queue.Queue()
    active = threading.Event()
    active.set()

    # START THREADS
    threads = []
    for _ in range(num_threads):
        t = threading.Thread(target=worker, args=(q, active))
        t.start()
        threads.append(t)

    try:

        # PUT STACK ITEMS ON QUEUE AND BLOCK UNTIL ALL TASKS ARE DONE
        for stack1_item in stack1:
            q.put(stack1_item)
        q.join()
        for stack2_item in stack2:
            q.put(stack2_item)
        q.join()

        # STOP WORKER LOOP IN EVERY THREAD
        #for _ in threads:
            #q.put(None)
        active.clear()

        # WAIT UNTIL ALL THREADS TERMINATE
        for t in threads:
            t.join()

    except KeyboardInterrupt:
        print(traceback.format_exc())
        print('remaining queue: ' + str(list(q.queue)))
        #for _ in threads:
            #q.put(None)
        active.clear()
        for t in threads:
            t.join()


if __name__ == '__main__':
    main()

如果我按原样运行脚本(没有KeyboardInterrupt),它将不会终止。我必须杀死信号。但是,如果我评论/取消注释以下行(不使用事件并以文档方式执行...)

comment / worker / if not active.is_set():
uncomment / worker / #if worker_item == None:
comment / main / active.clear()
uncomment / main / #for _ in threads:
                       #q.put(None)
comment / main / except / active.clear()
uncomment / main / except / #for _ in threads:
                                #q.put(None)

它会以退出代码0退出。为什么?

为什么要将Nones放入必要的队列? 如果不将Nones放入队列,会有什么解决方案?

1 个答案:

答案 0 :(得分:0)

有两种类型的线程:守护进程和非守护进程。默认情况下,所有线程都是非守护进程。只要至少有一个非守护程序线程,该进程就会保持活动状态。

这意味着要停止该过程,您必须:

  1. 停止所有线程(这是您注释掉的代码所做的工作,使用None让工作人员摆脱q.get()中的无限等待;或
  2. 创建worker守护程序线程,在这种情况下,一旦主线程停止,进程就会停止(如果你想确保工作人员完成任务,这将需要额外的注意)。