Skip to content
Free public beta until December 4, 2026: every course is open, no card needed. After that, the first chapter of each course stays free and full access is a one-time $39 ($19 for up to 100 founding members chosen for helpful feedback). Your progress is kept. How the beta works

Ways to run code at the same time

Threads, processes, a pool from concurrent.futures, or asyncio. Whether the program waits on the network or works the CPU decides which.

First ask what is the program waiting for?

  • It mostly waits on the network or disk (downloading pages, calling APIs) → concurrency helps. A ThreadPoolExecutor is the least code; asyncio with a TaskGroup scales to thousands of connections when your libraries are async.
  • It's busy doing arithmetic → threads won't speed it up on a standard build, because of the GIL. Use processes (ProcessPoolExecutor), or NumPy, which does the work in compiled code.
  • You only have a few slow calls → keep it sequential. It's simpler to read, test and debug.

The free-threaded build of CPython removes the GIL, so threads can then run Python code in parallel too, but most programs don't need it.

Choosing

Waiting at the same time is different from working at the same time.

Threads and processes

Section

Threads

Good for waiting; shared data needs a lock.

Section

Processes

Real parallelism for CPU work, at the cost of copying data between them.

One pool interface for both, with submit, map and as_completed.

Why CPU-bound threads don't speed up on a standard build.

asyncio

await hands control back to the event loop while you wait.

Start several coroutines at once and wait for them all.

Timeouts, limits on how many run at once, and never calling blocking code inside async.