Thread-Safe Queues (queue)

A queue.Queue is a thread-safe container for passing items between threads — producers put() work in and consumers get() it out, with all the locking handled for you.

Learn Thread-Safe Queues (queue) in our free Python course — an interactive lesson with runnable examples, a practice exercise and a quick reference.

Part of the free Python course at LearnCodingFast — hands-on lessons with examples you run in your browser, plus practice exercises and a quick quiz.

It's the backbone of the producer/consumer pattern: one or more threads generate work, others process it, and the queue safely connects them. No manual locks required.

A FIFO queue.Queue hands items back in the order they went in. put(item) adds; get() removes and returns the oldest item; empty() and qsize() report state:

Here's the real power: a producer feeds items in while consumer threads pull them out. task_done() marks each item complete, join() waits until everything is processed, and a None "poison pill" tells each worker to stop:

The queue module ships two ordered variants. A LifoQueue is a stack (last in, first out). A PriorityQueue serves the smallest item first — put (priority, value) tuples and the lowest priority number wins:

Replace each ___ so the queue is filled, then fully drained into a list while marking each item done.

❌ Calling get() on an empty queue with no thread to fill it

❌ Forgetting task_done(), so join() never returns

If you have 3 worker threads, you must put 3 None sentinels — otherwise some workers never receive the stop signal and join() on them hangs.

Two worker threads pull numbers from a work queue, square them, and put the results on a second queue. Drain the result queue, sort it, and print the sum.

Lesson complete — you can pipe work between threads!

You can put() and get() safely from many threads, coordinate completion with task_done() and join() , stop workers with a poison pill, and choose LifoQueue or PriorityQueue when ordering matters.

🚀 Up next: contextlib — write your own context managers and tame resources elegantly.

Practice quiz

Why use queue.Queue instead of a plain list to pass work between threads?

  • It is faster than a list
  • It uses less memory
  • It is thread-safe — handles locking internally
  • It sorts items automatically

Answer: It is thread-safe — handles locking internally. Queue lets multiple threads put() and get() safely without you writing any locks.

A plain queue.Queue() serves items in what order?

  • FIFO (first in, first out)
  • LIFO (last in, first out)
  • Random
  • Sorted

Answer: FIFO (first in, first out). queue.Queue is FIFO — the oldest item put in comes out first.

After putting 1, 2, 3 into a LifoQueue, what does draining it produce?

A LifoQueue is a stack: last in, first out, so you get [3, 2, 1].

A PriorityQueue serves which item first?

  • The most recently added
  • The one with the smallest priority value
  • The one with the largest priority value
  • A random one

Answer: The one with the smallest priority value. Put (priority, value) tuples; the lowest priority number is served first.

What happens if you call get() on an empty Queue with no thread to fill it?

  • It blocks (waits) forever
  • It returns None
  • It raises immediately
  • It returns an empty string

Answer: It blocks (waits) forever. By default get() blocks until an item is available — in single-threaded code that hangs forever.

What does a worker do with each item it finishes to coordinate with join()?

  • q.done()
  • q.finish()
  • q.task_done()
  • q.complete()

Answer: q.task_done(). Each consumer calls q.task_done(); q.join() unblocks once task_done count equals items put.

What does q.join() do?

  • Merges two queues
  • Blocks until every queued item has been marked task_done()
  • Starts all worker threads
  • Empties the queue

Answer: Blocks until every queued item has been marked task_done(). join() waits until the queue is fully drained and processed before returning.

What is a 'poison pill' in the producer/consumer pattern?

  • A corrupted item
  • An exception type
  • A priority of zero
  • A sentinel value (often None) that tells a worker to stop

Answer: A sentinel value (often None) that tells a worker to stop. Workers loop on get(); pulling a None sentinel signals them to break and exit cleanly.

If you have 3 worker threads, how many poison pills must you put?

  • 1
  • 3
  • 2
  • It doesn't matter

Answer: 3. One sentinel per worker — fewer pills means some workers never get the stop signal and join() hangs.

How can you make get() raise instead of hanging on an empty queue?

  • q.get(block=True)
  • q.get(wait=False)
  • q.get(timeout=1)
  • q.get(strict=True)

Answer: q.get(timeout=1). q.get(timeout=1) raises queue.Empty after the timeout instead of blocking forever.