Filter concepts by levelShowing all levels.

Python · Concurrency concepts

Concurrency concepts

The vocabulary and the one distinction — what the work is actually doing while it waits — that decides every tool choice after it

  • Concurrency vs parallelismmaking progress on several things vs. literally doing several at once
  • CPU-bound vs I/O-boundbusy computing, or mostly waiting — decides the right tool
  • Threads, processes, asyncthree different approaches, three different tradeoffs
  • Async is one threadconcurrency without parallelism — cooperative, not preemptive

1.Concurrency vs parallelism

Concurrency is structuring a program to deal with multiple tasks by interleaving them — not necessarily at the same instant. Parallelism is actually running multiple tasks at the same instant, on separate CPU cores.

Key features

  • Concurrency: structuring work as multiple tasks that can be interleaved or overlapped
  • Parallelism: actually executing multiple tasks at the same physical instant
  • One CPU core can be concurrent (switching between tasks) but never parallel
  • Multiple cores make true parallelism possible — concurrency does not require them
  • Python threads give concurrency for I/O-bound work; true CPU parallelism needs processes

The four combinations, and what gets you there in Python

The four combinations, and what gets you there in Python
PropertyNot concurrentConcurrent
Not parallelone task, run to completionthreads/asyncio interleaving on one core
Parallel(not a real combination)multiprocessing across several cores
Interleaved vs simultaneous

Concurrency

  • Multiple tasks in progress, interleaved
  • Works even on a single CPU core
  • Python threads/asyncio: this, not parallelism

Parallelism

  • Multiple tasks running at the same instant
  • Requires multiple CPU cores
  • Python: needs multiprocessing, not threading
  • Concurrency
    • Multiple tasks in progress, interleaved
    • Works even on a single CPU core
    • Python threads/asyncio: this, not parallelism
  • Parallelism
    • Multiple tasks running at the same instant
    • Requires multiple CPU cores
    • Python: needs multiprocessing, not threading

Command

python
# concurrency: tasks make progress by interleaving (threads, asyncio)
# parallelism: tasks run at the same instant (multiprocessing, multiple cores)

Concurrency is interleaving multiple tasks; parallelism is running them at the same instant. Python threads: concurrency. Python processes: parallelism.

2.CPU-bound vs I/O-bound workloads

A CPU-bound task spends its time computing — the processor is the bottleneck. An I/O-bound task spends its time waiting on something else, like a network call or disk read — the processor sits idle during the wait.

Key features

  • CPU-bound: bottleneck is computation — number crunching, image processing, parsing
  • I/O-bound: bottleneck is waiting — network calls, disk reads, database queries
  • CPU-bound Python code needs multiprocessing (separate cores) to actually speed up
  • I/O-bound Python code speeds up with threads (or asyncio) despite the GIL
  • The GIL is released during a blocking I/O call, which is exactly why threads help there

Which workload, and what actually helps in Python

Which workload, and what actually helps in Python
WorkloadExampleWhat helps
CPU-boundimage resizing, numeric simulation, parsing a huge filemultiprocessing (separate cores)
I/O-boundHTTP request, database query, reading a file from diskthreading or asyncio (overlap the waits)
Mixeddownload a file, then compress itthreads/asyncio for the download, processes for the compression
Where the bottleneck actually is

CPU-bound

  • Processor is busy the whole time
  • Speeds up only with more cores (multiprocessing)
  • Example: computing a checksum over a big file

I/O-bound

  • Processor sits idle, waiting on something external
  • Speeds up by overlapping waits (threading, asyncio)
  • Example: fetching ten URLs over the network
  • CPU-bound
    • Processor is busy the whole time
    • Speeds up only with more cores (multiprocessing)
    • Example: computing a checksum over a big file
  • I/O-bound
    • Processor sits idle, waiting on something external
    • Speeds up by overlapping waits (threading, asyncio)
    • Example: fetching ten URLs over the network

Command

python
# CPU-bound: the loop itself is the cost — use multiprocessing
def cpu_bound(n):
    return sum(i * i for i in range(n))

# I/O-bound: the wait is the cost — use threading or asyncio
def io_bound(url):
    return requests.get(url)   # blocks on the network, not the CPU

CPU-bound: the processor is the bottleneck, use multiprocessing. I/O-bound: waiting is the bottleneck, use threading or asyncio.

3.Multithreading, multiprocessing, and pools — an overview

Python offers four building blocks for running more than one thing at once: a single Thread, a single Process, a ThreadPool that reuses a fixed set of threads, and a ProcessPool that reuses a fixed set of processes.

Key features

  • threading.Thread: one thread, shares memory with the main process, good for I/O-bound work
  • multiprocessing.Process: one separate process, its own memory, good for CPU-bound work
  • A thread pool reuses a fixed number of threads across many short-lived I/O-bound tasks
  • A process pool reuses a fixed number of processes across many CPU-bound tasks
  • concurrent.futures wraps both pool types behind one common Executor interface

The four building blocks, side by side

The four building blocks, side by side
ToolUnit of workBest for
threading.Threadone threada single I/O-bound background task
multiprocessing.Processone processa single CPU-bound task, run on another core
ThreadPoolExecutorreused thread poolmany I/O-bound tasks (many URLs, many files)
ProcessPoolExecutorreused process poolmany CPU-bound tasks (many images, many rows)

Command

python
# single unit
threading.Thread(target=fn).start()
multiprocessing.Process(target=fn).start()

# reused pool, many units of work
with ThreadPoolExecutor(max_workers=N) as ex:
    ex.map(fn, items)
with ProcessPoolExecutor(max_workers=N) as ex:
    ex.map(fn, items)

Thread/Process = one worker for one job. ThreadPoolExecutor/ProcessPoolExecutor = a reused crew for many jobs, one Executor interface for both.

4.Asynchronous programming

Asynchronous programming runs many tasks on a single thread by explicitly pausing at each await point and letting another task run. Unlike threading, the switches happen only where the code says await — never mid-line.

Key features

  • async def defines a coroutine; it only runs when awaited or scheduled on an event loop
  • Control switches to another task only at an await — never at an arbitrary point
  • One thread runs many coroutines — no extra OS threads or processes needed
  • Good fit for many concurrent I/O-bound waits (thousands of open connections)
  • Not a fit for CPU-bound work — a coroutine that never awaits blocks the whole event loop

Command

python
import asyncio

async def fetch(url):
    await asyncio.sleep(0.1)   # yields control at this exact point
    return f"data from {url}"

async def main():
    results = await asyncio.gather(fetch("a"), fetch("b"))
    print(results)

asyncio.run(main())

async/await runs many tasks on one thread, switching only at explicit await points — good for many concurrent waits, useless for CPU-bound work.

Remember

  • Concurrency is structuring work to make progress on multiple tasks; parallelism is literally running more than one at the same instant.
  • CPU-bound means busy computing; I/O-bound means mostly waiting — this single distinction picks the right concurrency tool.
  • Threading, multiprocessing, and async are three different tools for three different shapes of workload, not interchangeable defaults.
  • Async concurrency runs on one thread — it interleaves waiting tasks, it does not add CPU parallelism by itself.

Pro tip

  • Ask "what is this code doing while it works?" before picking a tool — computing points at multiprocessing, waiting points at threading/async.
  • A thread pool and a process pool share the same concurrent.futures API — the executor class you choose is the real decision.
  • Async and threading both help I/O-bound work; they are alternatives to each other, not usually combined for the same task.
  • Parallelism requires multiple CPU cores actually in use; concurrency does not — a single core can be concurrent without being parallel.

Next up

  • Threading

Sources

  • Concurrent Execution — docs.python.org/3/library/concurrency.html
  • Every example executed on Python 3.14.3 · checked 2026-08-21

Spotted a mistake on this page? Tell us.