NOTE
2.14 Goroutines
What goroutines are, how `go f(...)` starts concurrent execution, how their stacks differ from OS-thread stacks, and how the runtime schedules them.
This is a historical learning note and may contain outdated or incomplete understanding.
1. What Is a Goroutine?
A goroutine is a lightweight execution unit managed by the Go runtime. It is not an operating-system thread, although goroutines ultimately execute on OS threads.
Compared with creating one OS thread per task, goroutines are cheaper to create and can start with small stacks that grow and shrink as needed. This makes it practical for Go programs to use large numbers of concurrent tasks.
2. Starting a Goroutine
func work(x, y int) {
fmt.Println(x + y)
}
func main() {
go work(1, 2)
}
go work(1, 2) evaluates the function value and arguments in the calling goroutine, then schedules a new goroutine to execute the call.
A goroutine has its own stack and execution state, but it shares the process address space with other goroutines. Shared memory therefore still requires synchronization.
3. Lifetime
Starting a goroutine does not make its work durable. If main returns, the process exits even if other goroutines are still running.
Production code should normally have an explicit lifetime mechanism such as:
sync.WaitGroupfor completion;context.Contextfor cancellation;- channel ownership/closure protocols;
- an application-level supervisor or worker lifecycle.
4. Runtime Implementation
Historically, compiler output for a go statement lowers into runtime machinery that allocates/initializes a goroutine and places it into scheduling queues. Exact runtime symbols and layouts are implementation details and can change between Go releases.
The stable mental model is:
- create a runnable
G; - enqueue it for execution;
- the runtime scheduler assigns it to a
Pand an OS threadMwhen resources are available.
See GMP.
5. Goroutines Are Not Automatically Safe
Cheap concurrency does not remove correctness problems. Goroutines can still:
- race on shared state;
- deadlock;
- leak while blocked forever;
- overload downstream systems when created without bounds.
Use concurrency because the workload benefits from it, not because goroutines are inexpensive.