Compare

Concurrency vs parallelism

Concurrency means several tasks are in progress at once: one can start before another has finished. Parallelism means they run at the very same instant, on different cores. Every parallel program is concurrent; a concurrent one is parallel only if the hardware and the runtime let it be.

Concurrency on one thread

Two Tokio tasks share a single thread. Both are in progress at once, but at any moment only one runs: they switch at each await.

RustTokio tasks switch only at .await

Two tasks share one thread. They take turns, and every switch happens at an .await.

012345678main: spawn a, bspawna.awaitmain: b.awaitmain: returntask a: step 1, yieldtask a: ready, waits for the threadreadytask a: step 2, yieldtask a: step 3, yieldreadytask b: step 1, yieldtask b: step 2, yieldtask b: ready, waits for the threadreadytask b: step 3, yield
Play it step by step

Threads, but still not parallel

Two Python threads crunch numbers. They are separate threads, yet the GIL lets only one run Python code at a time, so they take turns on one core.

PythonWhy Python threads don't run in parallel (the GIL)

Two threads crunch numbers. They take turns holding the GIL, so only one core is ever busy.

012345678main: start a, bstartjoin()main: returnthread a: crunchthread a: waits for the GILwaitsthread a: crunchthread a: waits for the GILwaitsthread a: crunch, donedonethread b: waits for the GILwaitsthread b: crunchthread b: waits for the GILwaitsthread b: crunchthread b: waits for the GILwaitsthread b: crunch, donedoneGIL: held by aaGIL: held by bbGIL: held by aaGIL: held by bbGIL: held by aaGIL: held by bb
Play it step by step

Parallel: two cores at once

The same Python code on the free-threaded build. With no GIL, both threads crunch at the same time, and two cores are busy.

PythonFree-threaded Python has no GIL

The same code on the free-threaded build. With no GIL, both threads crunch at the same time on two cores.

01234567main: start a, bstartjoin()main: returncrunch, donecrunch, done
Play it step by step

Which one you need

Waiting on networks, disks or users? Concurrency is enough: tasks overlap their waiting even on one thread. Crunching numbers? You need parallelism: separate cores, through threads without a GIL, processes, or a pool.

Common questions

What's the difference between concurrency and parallelism?

Concurrency is several tasks in progress at once, taking turns. Parallelism is several tasks running at the same instant, on different cores. Concurrency is about structure; parallelism is about hardware.

Can you have concurrency without parallelism?

Yes. Tokio's current_thread runtime runs many tasks on one thread, switching at each .await. JavaScript's event loop does the same. They overlap waiting, not computing.

Do threads always run in parallel?

No. Python threads with the GIL take turns: two CPU-bound threads used one CPU's worth of time between them in our tests. On the free-threaded build, the same code used two.

Every timeline here is checked against a real program in CI. How we verify