Thread Communication (Queue)
flowchart LR
A["producer thread"] --> B["q.put(item)"]
B --> C{"maxsize reached?"}
C -->|yes| D["producer BLOCKS -- back-pressure"]
C -->|no| E["item queued"]
E --> F["q.get() in a worker"]
F --> G{"empty?"}
G -->|yes| H["worker BLOCKS -- no polling, no spinning"]
G -->|no| I["process it"]
I --> J["q.task_done()"]
J --> K["q.join() returns when every put has a matching task_done"]
L["a sentinel value"] --> M["is how workers are told to stop"]
Why Queue?
Section titled “Why Queue?”queue.Queue is thread-safe.
Use it to:
- pass tasks from producer threads to worker threads
- avoid manual lock management
Producer-consumer example
Section titled “Producer-consumer example”import threading
import queue
import time
q = queue.Queue()
def producer():
for i in range(5):
q.put(i)
print("produced", i)
q.put(None) # sentinel
def consumer():
while True:
item = q.get()
try:
if item is None:
break
print("consuming", item)
time.sleep(0.2)
finally:
q.task_done()
t1 = threading.Thread(target=producer)
t2 = threading.Thread(target=consumer)
t1.start(); t2.start()
t1.join()
q.join() # wait until all tasks are marked done
print("All tasks processed")- Use a sentinel (
None) to stop consumers. - Always call
task_done()(often in afinally).
Check yourself
Section titled “Check yourself”-
A consumer calls `q.get()` on an empty `queue.Queue`. What happens?
It parks, using no CPU, until something is put. `get_nowait()` is the version that raises `Empty` instead.
pch.quizShowAnswer
C — It blocks until an item arrives — It parks, using no CPU, until something is put. `get_nowait()` is the version that raises `Empty` instead.
-
What does `maxsize` give you beyond a memory limit?
With `maxsize=0` (unbounded) a producer faster than its consumer will grow the queue until memory runs out. A bound makes the producer wait instead.
pch.quizShowAnswer
B — Back-pressure — a full queue blocks the producer — With `maxsize=0` (unbounded) a producer faster than its consumer will grow the queue until memory runs out. A bound makes the producer wait instead.
-
What is the point of `task_done()` and `join()`?
`join()` returns when every `put` has a matching `task_done`. Note it tracks completion of the WORK, not merely that the queue is empty.
pch.quizShowAnswer
B — Knowing when every item that was put has been fully handled — `join()` returns when every `put` has a matching `task_done`. Note it tracks completion of the WORK, not merely that the queue is empty.
-
How do you tell worker threads to stop?
Each worker takes one sentinel and exits, so every worker gets exactly one. A queue has no close, and killing threads is not possible.
pch.quizShowAnswer
B — Put one sentinel value per worker on the queue — Each worker takes one sentinel and exits, so every worker gets exactly one. A queue has no close, and killing threads is not possible.
🧪 Try It Yourself
Section titled “🧪 Try It Yourself”Exercise 1 – Put and Get
Section titled “Exercise 1 – Put and Get”Exercise 2 – Producer-Consumer
Section titled “Exercise 2 – Producer-Consumer”Exercise 3 – Queue Size
Section titled “Exercise 3 – Queue Size”pch.coffeeTagline
pch.coffeeCtapch.feedbackHeading
pch.feedbackSubheading