# Thread vs Coroutine

URL: https://softwaredictionary.org/compare/thread-vs-coroutine
Last updated: 2026-10-06

In short: The operating system schedules threads and can interrupt them at any moment; coroutines pause only where they choose, so thousands can share one thread.

## What is the difference between a thread and a coroutine?

A thread is a unit of execution that the operating system schedules: it has its own stack and registers, shares its process's memory with the other threads, and can run in parallel with them on a multi-core CPU. A coroutine is a function that can pause partway, at points it marks itself, and later resume with its local variables intact. Coroutines run on top of threads: a scheduler inside the program, often an event loop, decides which ready coroutine runs next.

The essential difference is who decides when to switch. Threads are scheduled preemptively: the operating system can stop one at any instruction and run another, which is why shared data needs locks. Coroutines use cooperative multitasking: each runs until it reaches a pause point such as `await` or `yield`, so a switch saves only a little state and needs no help from the operating system. A thread reserves its own stack, often a megabyte or more, while a coroutine keeps just the state it needs between pauses, so a program that manages thousands of threads can manage millions of coroutines.

They are less rivals than layers. Coroutines suit I/O-bound work, such as a server holding thousands of network connections that spend most of their time waiting; threads suit CPU-bound work that should use several cores, and blocking calls that would otherwise stall a coroutine's thread. Many runtimes combine the two: Kotlin dispatches coroutines onto thread pools, Python's asyncio hands blocking calls to a thread with `asyncio.to_thread`, and Java's virtual threads, final since Java 21, bring the same cheap switching to code written with the ordinary thread API.

A common misconception is that coroutines run code in parallel. On one thread they only take turns, so a coroutine doing long CPU work without pausing blocks every other coroutine on that thread. Another is that cooperative switching removes every race: code between two pauses can't be interrupted, but shared state can still change across an `await`. Go's goroutines, despite the name, behave more like lightweight threads, since the runtime can preempt them and runs them in parallel across cores.

| Aspect | Thread | Coroutine |
| --- | --- | --- |
| Scheduled by | The operating system kernel | The program: an event loop or a runtime scheduler |
| Switching | Preemptive: can be interrupted at any instruction | Cooperative: only at pause points such as `await` or `yield` |
| Memory each | Its own stack, often a megabyte or more reserved | A small object holding the state it needs between pauses |
| How many | Hundreds or thousands before memory and switching costs bite | Thousands to millions on a handful of threads |
| Parallelism | Run in parallel on several CPU cores | Take turns on one thread; parallel only when spread over several |
| Shared data | Needs locks, since a switch can happen mid-update | Safe between pauses; state can still change across an `await` |
| Best for | CPU-heavy work and blocking calls with no async version | Many concurrent waits: network calls, timers, streams |
| Examples | `threading.Thread` in Python, `Thread` in Java, `std::thread` in C++ | Python `async def`, Kotlin `suspend`, C++20 `co_await`, Lua `coroutine.yield` |

## Choose Thread when

- The work is CPU-bound and should use several cores.
- You call blocking libraries that have no asynchronous version.
- A long task must not freeze the others, even if it never pauses.

## Choose Coroutine when

- The program spends most of its time waiting on the network, the disk or timers.
- You need thousands of concurrent tasks, such as open connections.
- You want switches to happen only at known points, so less code needs locks.
- Your language or framework is built around async/await, as with Python's asyncio or Kotlin.

## Frequently asked questions

**Are coroutines faster than threads?**

For many concurrent waits, yes: creating and switching coroutines is far cheaper, and each one uses much less memory. For CPU-heavy work they are no faster, because coroutines on one thread don't run in parallel.

**Can coroutines run on more than one thread?**

Yes. Many runtimes spread coroutines over a pool of threads; Kotlin, for example, runs them on dispatchers backed by thread pools. A single coroutine still runs on one thread at a time, though it may resume on a different one after a pause.

**Are goroutines threads or coroutines?**

Something in between. Goroutines are lightweight threads managed by the Go runtime: cheap to create like coroutines, but the runtime can preempt them and runs them in parallel across cores, so shared data still needs synchronization.

---

Software Dictionary: https://softwaredictionary.org/ · https://softwaredictionary.org/llms.txt
