Queue.shutdown rejects new work while allowing already queued jobs to finish when immediate is false.
Make this comfortable
Python Queue.shutdown: drain accepted work before workers exit
Operation contract
The intake accepts two receipt IDs, then closes. A worker drains both, acknowledges each with task_done, and exits on ShutDown only after the queue empties. A later producer also receives ShutDown. The join call checks that accepted jobs were acknowledged, not merely dequeued.
Failure boundary
An immediate shutdown can release join without processing every queued item; do not use it as a success signal. Queue.empty and qsize are snapshots, not admission checks. A worker exception before task_done can leave join waiting forever unless the owner records failure and resolves the unfinished count.
Working program
from queue import Queue, ShutDown
from threading import Thread
receipts = Queue(maxsize=3)
processed = []
def write_receipts():
while True:
try:
receipt_id = receipts.get()
except ShutDown:
return
try:
processed.append(receipt_id)
finally:
receipts.task_done()
worker = Thread(target=write_receipts)
worker.start()
for receipt_id in ("north-47", "north-48"):
receipts.put(receipt_id)
receipts.shutdown()
receipts.join()
worker.join()
try:
receipts.put("north-49")
except ShutDown:
print("late_rejected", True)
print("processed", sorted(processed))Output
late_rejected True
processed ['north-47', 'north-48']Costs and limits
Accepted work and acknowledgements cost O(n). A bounded queue caps pending references at its capacity; the example also retains processed IDs for inspection.
Common Mistakes
- Do not confuse immediate shutdown with completed work.
- Call task_done once for each successful get, even on a processing failure path.
- Do not use qsize to decide whether a put will block.
Connected lessons
python
queue-graceful-shutdown
