The queue Module
The queue module provides thread-safe FIFO queues that remove locks from your application code entirely. Instead of protecting a list with a lock, producers put items and consumers get items; the queue itself serializes access and blocks when it is empty or full. This is the cleanest fix for the producer-consumer pattern.
Basic Usage
import queue
import threading
import time
q = queue.Queue()
def producer():
for i in range(5):
q.put(i)
time.sleep(0.1)
q.put(None) # sentinel: no more items
def consumer():
while True:
item = q.get() # blocks until an item is available
if item is None:
break
print("processed", item)
q.task_done()
ct = threading.Thread(target=consumer)
ct.start()
producer()
ct.join()
put and get Variants
| Call | Behavior |
|---|---|
put(item) | blocks while the queue is full (bounded only) |
put(item, block=False) | raises Full if full |
put(item, timeout=1) | raises Full after 1s |
get() | blocks while empty |
get(block=False) | raises Empty if empty |
get(timeout=1) | raises Empty after 1s |
A Queue(maxsize=10) is bounded: when ten items are queued, producers block. That backpressure is a feature - it stops a slow consumer from letting memory grow without bound.
task_done and join
task_done() marks one item as fully processed, and q.join() blocks until every item that was ever put has been marked done. Together they give the main thread a "work finished" signal without any extra counter:
q = queue.Queue(maxsize=3)
# ... put 10 jobs, run 4 consumer threads ...
q.join() # return only after all jobs were task_done()d
Specialized Queues
queue.LifoQueue- last-in-first-out (a stack).queue.PriorityQueue- items with the smallest value come out first; pair items as(priority, seq, payload)so equal priorities do not compare the payloads.
Summary
Prefer queue.Queue over hand-rolled list-plus-lock code: it handles blocking, bounds, and task accounting for you. Send a sentinel or use join() to signal the end of work, and choose PriorityQueue when processing order matters.