What is a goroutine, if not a thread?¶
Level: 201 · anyone moving between Go and a language with real threads
Stub — a question and its expected answers, not yet a lesson. No program behind this page has been written or run, so nothing on it has been through the check that backs every finished page. The table is what the finished lesson is expected to find, and every cell is unverified until a program prints it. (Not machine-checked here.)
One line: A goroutine, a Java virtual thread and a Rust async task are all user-space units of execution that a runtime multiplexes onto a few operating-system threads — cheap to start, cheap to block, and invisible to the operating system — and the price is that a call which blocks the underlying thread without telling the runtime stalls every other unit sharing it.
The question¶
go f() starts something that looks like a thread and is not one. Neither is Thread.ofVirtual().start(...) nor tokio::spawn. What do they share, where do they run, and what do they cost? The page starts all three, then asks the question that separates them from threads: what happens when one of them makes a blocking system call?
What each language is expected to say¶
| Expected answer, to be verified by the program | |
|---|---|
| Rust | no green threads since 2014; async tasks on a runtime are the equivalent, and a blocking call inside one stalls its worker thread |
| Go | goroutines on GOMAXPROCS threads; the runtime moves a blocked goroutine off its thread, so a blocking syscall costs a thread, not the program |
| C | none: a thread is a thread, and any lighter unit is a library (ucontext, or a coroutine library) |
| C++ | none in std; C++20 coroutines are a building block without a scheduler |
| Java | virtual threads (Java 21) on a ForkJoinPool of carriers; a synchronized block used to pin the carrier, fixed in Java 24 |
| Python | asyncio tasks on one thread; threading threads are real ones |
What the programs have to show¶
- ten thousand units started and joined, printing the count and the number of OS threads the process had at the peak
- one unit doing a blocking read on a pipe that is never written, while the others keep printing — or stop
- a Real runs fence with start time and memory for a thousand of each
See also¶
- Before this: Why reuse a thread at all?
- After this: What does a sleep promise, and what does a yield?
- What does one blocking call do to every other task?
- How many threads can you start?
- The Go library's Goroutines are cheap ↗
- The Go library's A goroutine has no handle ↗
- The Rust library's Tasks ↗
- Concepts: Goroutine · Virtual thread · Green threads and M:N scheduling · Task (async) · Coroutine · Context switch · Scheduler