Python threads and queue example
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.
Introduction
In Python, threads and queues are commonly used together to build safe producer-consumer systems. Threads let several tasks make progress at the same time, while queue.Queue provides a thread-safe way to pass work items from producers to workers without hand-writing locks around shared lists.
This pattern is especially useful for I/O-bound jobs such as downloading files, reading sockets, or processing background tasks. It is much less useful for CPU-bound parallelism because the Global Interpreter Lock limits how much pure Python bytecode can run truly in parallel.
Why queue.Queue Matters
The main value of queue.Queue is not that it stores items. Any list can do that. Its real value is synchronized access. Producers can put work into the queue, consumers can get work out, and the queue manages the internal locking so threads do not corrupt shared state.
It also gives you useful coordination features:
- '
get()blocks until work is available,' - '
task_done()marks an item as finished,' - '
join()lets the main thread wait until all queued work has been processed.'
That is the foundation of a clean threaded pipeline.
A Producer-Consumer Example
The example below starts three worker threads, feeds them jobs, and uses a sentinel value of None to tell each worker when to stop.
A few details matter here. Each call to get() must eventually be matched with task_done(). The sentinel is placed once per worker so every worker gets a chance to exit. Finally, join() on the queue waits for all submitted items, including sentinels, to be acknowledged.
Choosing the Right Thread Model
This design works well when the task spends significant time waiting on external resources. For example, if each worker is reading from an API or writing files to disk, threads can hide that waiting time and improve overall throughput.
If the workload is CPU-heavy, threads are usually the wrong scaling strategy in CPython. In that case, multiprocessing or native code that releases the GIL is often a better option.
Bounded Queues and Backpressure
You can also give the queue a maximum size:
That creates backpressure. If producers submit items faster than workers can consume them, put() will block once the queue fills up. This is useful when you want to prevent unbounded memory growth in long-running services.
Without a limit, a fast producer can flood the process with pending items even though workers are far behind.
Common Pitfalls
- Forgetting
task_done(). If you usequeue.join(), one missingtask_done()can make the program wait forever. - Using one sentinel for multiple worker threads. Only one worker exits, while the others keep blocking on
get(). - Sharing a plain list between threads instead of using
queue.Queue, which invites race conditions and extra lock code. - Expecting Python threads to speed up CPU-bound work in CPython. They are mainly helpful for I/O-bound concurrency.
- Not joining threads before process exit, which can leave background work incomplete or produce inconsistent shutdown behavior.
Summary
- '
queue.Queueis the standard thread-safe handoff mechanism for Python worker threads.' - The producer-consumer pattern is a good fit for I/O-bound background work.
- Use
task_done()andjoin()to coordinate completion cleanly. - Send one sentinel per worker when it is time to stop.
- For CPU-bound parallelism, prefer processes rather than threads in CPython.
Related reading
- Python update a key in dict if it doesn't exist
- Pythonic way to check if a list is sorted or not
- Pythonic way to check if a list is sorted or not
- Pythonic way to find maximum value and its index in a list?
- Python time.sleep vs event.wait
- Python Tornado - Asynchronous Request is blocking
- Python truncate a long string
- Python try...except comma vs 'as' in except

DSA Fundamentals
Master algorithmic patterns and data structures through hands-on LeetCode-style problems - from arrays and hashing to dynamic programming and advanced graphs.
View the courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Data Structures & Algorithms practice on Codemia
Step through 300 algorithm problems with animated visualisers that show the data structure changing as the code runs.