2
votes

I was reading about Queue in the Python documentation and this book, and I don't fully understand why my thread hangs. I have the following mcve:

from threading import Thread
import queue


def print_number(number_queue_display):

    while True:
        number = number_queue_display.get()
        print(number)
        number_queue_display.task_done()


number_queue = queue.Queue()
printing_numbers = Thread(target=print_number, args=(number_queue,),)
printing_numbers.start()

number_queue.put(5)
number_queue.put(10)
number_queue.put(15)
number_queue.put(20)

number_queue.join()
printing_numbers.join()

The only time it works is if I set the thread to daemon like so:

printing_numbers.setDaemon(True)

but that's because as stated in the Python documentation, the program will exit when only the daemon threads are left. The Python docs example for Queue doesn't use a daemon thread.

A thread can be flagged as a “daemon thread”. The significance of this flag is that the entire Python program exits when only daemon threads are left.

Even if I were to remove the two joins(number_queue.join() printing_numbers.join()), it still hangs, but I'm unsure of why.

Questions:

  1. Why is it hanging?
  2. How do I keep it as a non-daemon thread, but prevent it from hanging?
1

1 Answers

2
votes

print_number() is running an infinite loop - it never exits, so the thread never ends. It sits in number_queue_display.get() forever, waiting for another queue item that never appears. Then, since the thread never ends, printing_numbers.join() also waits forever.

So you need some way to tell the thread to quit. One common way is to put a special "sentinel" value on the queue, and have the thread exit when it sees that. For concreteness, here's a complete program, which is very much the same as what you started with. None is used as the sentinel (and is commonly used for this purpose), but any unique object would work. Note that the .task_done() parts were removed, because they no longer serve a purpose.

from threading import Thread
import queue

def print_number(number_queue_display):

    while True:
        number = number_queue_display.get()
        if number is None:
            break
        print(number)


number_queue = queue.Queue()
printing_numbers = Thread(target=print_number, args=(number_queue,),)
printing_numbers.start()

number_queue.put(5)
number_queue.put(10)
number_queue.put(15)
number_queue.put(20)
number_queue.put(None)  # tell the thread it's done

printing_numbers.join() # wait for the thread to exit