Creating Threads in python
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
Threads in Python are useful when work spends time waiting on I O, such as network requests, disk access, or external services. Creating a thread is straightforward with the threading module, but using threads well requires a clear idea of startup, shared state, and shutdown.
The Basic Thread API
The standard pattern is:
- define a target function
- create a
Thread - call
start() - call
join()when you need to wait for completion
start() launches the thread. join() blocks until it finishes. Without join(), the main program may continue before you expect.
Running Multiple Threads
If you have several independent I O-bound tasks, start several threads and then join them all.
This pattern is fine for small batches of work and helps keep network-heavy programs responsive.
Shared Data Requires Synchronization
Threads become dangerous when they modify shared mutable state without coordination. Even simple increments can race.
The lock protects the critical section so the final count stays correct.
Daemon Threads vs Regular Threads
Python threads can be marked as daemon threads:
Daemon threads are stopped abruptly when the main program exits. They are useful for nonessential background activity, but they are a poor choice for work that must flush files, finish network operations, or release resources cleanly.
For most application logic, regular threads plus explicit shutdown are safer.
Graceful Shutdown with Event
Long-running threads should listen for a stop signal.
This is much better than killing the process and hoping the thread was in a safe place to stop.
When to Use a Thread Pool Instead
If you need to run many similar tasks, ThreadPoolExecutor is often cleaner than managing raw Thread objects yourself.
Thread pools are easier to scale, easier to collect results from, and easier to use for batches of short jobs.
The GIL Question
A common misunderstanding is that threads automatically speed up every kind of Python work. They do not. For CPU-heavy pure-Python computation, the GIL limits parallel execution of Python bytecode.
Practical rule:
- I O-bound work: threads are a good fit
- CPU-bound work: prefer multiprocessing or native libraries
- huge numbers of socket tasks: consider
asyncio
Choosing the right concurrency model matters more than merely "using threads."
Common Pitfalls
Starting a thread and forgetting to join() it can make program flow look random, especially in short scripts.
Sharing mutable data without locks or queues introduces race conditions that are often intermittent and hard to reproduce.
Using daemon threads for critical work can lose data because the interpreter may exit before cleanup finishes.
Expecting threads to speed up CPU-bound Python code usually leads to disappointment because of the GIL.
Summary
- Create Python threads with
threading.Thread, thenstart()andjoin()them. - Threads are most useful for I O-bound tasks.
- Protect shared mutable state with synchronization primitives like
Lock. - Use
Eventfor graceful shutdown of long-running workers. - For large batches of similar work,
ThreadPoolExecutoris often cleaner than manual thread management.

