Python threads all executing on a single core
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.
Introduction
If you start several Python threads and your CPU usage still looks like one core, you are usually seeing the effect of the Global Interpreter Lock, or GIL, in standard CPython. Threads still help for many programs, but they do not automatically give CPU-bound Python code true parallel execution.
What the GIL Actually Does
In the default CPython runtime, only one thread can execute Python bytecode at a time inside a process. The interpreter switches between threads frequently, which creates concurrency, but not full parallelism for CPU-heavy pure Python work.
That means a program like this does not scale across cores the way people often expect:
All four threads run, but they mostly take turns holding the GIL. On a multi-core machine, this often looks like one saturated core rather than four.
When Threads Still Help
Threads are still useful for I/O-bound workloads because waiting on the network, disk, or a socket gives other threads a chance to run. For example:
This finishes in about two seconds, not eight, because the threads overlap while sleeping. The same pattern applies to HTTP requests, file downloads, and database calls.
How To Use Multiple Cores for CPU Work
For CPU-bound tasks, use separate processes instead of threads. Each process has its own Python interpreter and its own GIL, so the operating system can schedule them on different cores.
This is the standard fix when you want actual core-level parallelism in Python.
Cases Where Threads Can Run Better Than Expected
Some extension modules release the GIL while doing heavy native work. Numeric libraries are a common example. If a C or C++ extension drops the GIL, multiple threads may make progress at the same time even though the surrounding Python program is threaded.
That is why the right answer depends on where the real work happens:
- pure Python loops: threads usually do not scale across cores
- blocking I/O: threads are often fine
- native extensions that release the GIL: threads may scale better
So the question is not only "am I using threads," but also "what code is running inside those threads."
A Practical Rule of Thumb
Pick the concurrency tool based on the bottleneck:
- use
threadingfor I/O-bound work - use
multiprocessingor process pools for CPU-bound pure Python work - use
asynciowhen you have many I/O tasks and want structured async flow
You can also use concurrent.futures for a cleaner interface:
This gives you process-based parallelism without manually managing worker lifecycles.
Common Pitfalls
The most common mistake is benchmarking CPU work with threading and assuming Python is ignoring available cores. In standard CPython, that result is expected.
Another mistake is using threads to speed up a tight numeric loop written in Python. If the code does not release the GIL, you may add context-switch overhead without gaining throughput.
It is also easy to confuse concurrency with parallelism. Threads can improve latency and responsiveness even when only one thread executes Python bytecode at a time.
Finally, remember that process-based solutions have costs too: data must be serialized between processes, startup is slower, and shared mutable state becomes harder to manage.
Summary
- In standard CPython, the GIL prevents multiple threads from executing Python bytecode in parallel inside one process.
- Threads are still effective for I/O-bound workloads because they overlap waiting time.
- For CPU-bound pure Python code, use multiple processes to take advantage of multiple cores.
- Some native extensions release the GIL, so threaded performance depends on where the work actually happens.
- Choose concurrency tools based on the real bottleneck, not on threads alone.
Related reading
- Python threads and queue example
- Python time.sleep vs event.wait
- Python Tornado - Asynchronous Request is blocking
- Python Twisted wait for a variable to be filled by another event
- Python truncate a long string
- Python try...except comma vs 'as' in except
- Python what are the advantages of async over threads?
- Python's concurrent.futures Iterate on futures according to order of completion
.png&w=3840&q=75)
Tackling System Design Interview Problems
A short course that equips you with the skills to approach system design interviews methodically.
Start the free courseTrack what you have practised
A free account saves your progress, solutions and study plan across every problem on Codemia.
Interview Questions practice on Codemia
Over 8,000 real interview questions from top companies, searchable by company and role.