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.