Python · The language, not just syntax
When do you use threads, processes or asyncio?
Short answer
Use threads for blocking I/O with existing synchronous libraries, processes for CPU-bound work that must use multiple cores, and asyncio for high-concurrency I/O when the stack is async-aware.
The choice follows the bottleneck. Threads share memory and are cheap to start, but the GIL serialises Python bytecode. Processes have separate memory and their own GIL, so they achieve true CPU parallelism at the cost of startup time and inter-process communication.
from concurrent.futures import ProcessPoolExecutor, ThreadPoolExecutor
def fetch(url):
return client.get(url).status_code
def crunch(data):
return sum(value * value for value in data)
with ThreadPoolExecutor(max_workers=8) as pool:
statuses = list(pool.map(fetch, urls))
with ProcessPoolExecutor(max_workers=4) as pool:
totals = list(pool.map(crunch, chunks))Trade-offs
- Threads: shared memory, lower startup cost, but locks and race conditions
- Processes: true parallelism, isolation, but serialisation overhead and higher memory use
- asyncio: many concurrent I/O operations in one thread, but one blocking call stalls the loop
- Process pools need picklable payloads and careful handling of large results
Shared mutable state is the main hazard with threads. Prefer queues, immutable messages or per-thread state over locks around large critical sections. With processes, minimise data crossing the boundary because pickling and copying can dominate runtime.
Follow-ups you should expect
Interviewers use these to check whether the first answer was understanding or memorisation.
- 1Why does a process pool achieve parallelism that a thread pool cannot?
- 2What kinds of objects can be sent to a process pool?
- 3How does asyncio differ from threading for I/O?
- 4What are the risks of shared mutable state across threads?
Say it in the interview
I choose based on the bottleneck. For blocking I/O with synchronous libraries I use threads, because they overlap waits and the GIL is released. For CPU-bound work I use processes so each one has its own interpreter and GIL, accepting pickling and memory overhead. For very high-concurrency I/O with async libraries I use asyncio. I profile first, because the wrong choice adds complexity without improving throughput.
Use this as a model, not a script. Saying it in your own words is what makes it survive follow-up pressure.
Related questions
Reading is step one
Now say it out loud.
Understanding an answer and delivering it under pressure are different skills. Rehearse this topic in a live Python mock interview and get scored feedback on depth, clarity and confidence.