Understanding Go’s CSP Model: Goroutine, Channel, Scheduler

This article explains Go’s concurrency fundamentals, distinguishing concurrency from parallelism, describing the CSP model built on goroutines and channels, and detailing the underlying M‑P‑G scheduler architecture—including thread models, runqueues, and load balancing—providing a comprehensive overview for developers.

MaGe Linux Operations
MaGe Linux Operations
MaGe Linux Operations
Understanding Go’s CSP Model: Goroutine, Channel, Scheduler
Go is a language designed for concurrency; it is one of the few languages that implements concurrency at the language level, attracting countless developers worldwide.

Concurrency and Parallelism

Concurrency: two or more tasks are executed over a period of time. We do not care whether they run simultaneously at any specific instant; we only care that each task makes progress within a short interval.

Parallelism: two or more tasks are executed at the same moment.

Concurrency is a logical concept, while parallelism emphasizes the physical execution state. Concurrency "includes" parallelism.

(See Rob Pike’s PPT for details.)

Go’s CSP Concurrency Model

Go implements two forms of concurrency. The first is the commonly known multithreaded shared‑memory model, similar to Java or C++. The second, unique to Go and recommended by the language, is the CSP (Communicating Sequential Processes) model.

The CSP model, proposed around the 1970s, differs from traditional multithreading by communicating instead of sharing memory. Remember the mantra: "Do not communicate by sharing memory; instead, share memory by communicating."

Traditional thread models (e.g., Java, C++, Python) use shared memory with locks, leading to thread‑safe data structures such as java.util.concurrent. Go also supports this traditional model.

Go’s CSP model is realized through goroutine and channel:

goroutine is the unit of concurrency in Go, analogous to a thread.

channel is the communication mechanism between goroutine s, similar to a pipe in Linux.

Creating a goroutine is simple: just call the function. go f(); Channel communication uses channel <- data to send and <-channel to receive. Sending and receiving always occur in pairs, and both operations block until the counterpart is ready.

When two goroutine s block on a channel—one waiting to send, the other waiting to receive—they discover each other and the data transfer proceeds.

This illustrates the basic form of Go’s CSP concurrency model.

Implementation Principles of Go’s Concurrency Model

All concurrency models ultimately rely on operating‑system threads. The OS separates user space and kernel space; kernel space manages CPU, I/O, and memory resources, while user space programs must request these resources via system calls or libraries.

Programming languages typically implement user‑level threads, which differ from kernel threads (KSE).

User‑Level Thread Model

Multiple user‑level threads map to a single kernel thread; thread creation, termination, and scheduling are handled by the runtime itself.

Kernel‑Level Thread Model

This model directly uses OS kernel threads; all thread operations are performed by the kernel. C++ follows this model.

Two‑Level Thread Model

This hybrid model sits between user‑level and kernel‑level models. A process may have multiple kernel threads, but user‑level threads are scheduled onto these kernel threads by the runtime.

Go’s thread model is a special two‑level model, often called the “MPG” model.

Go Thread Implementation Model MPG

M

stands for Machine and maps to a kernel thread. P stands for Processor, representing the execution context needed by an M to run user‑level code. G stands for Goroutine, a lightweight thread. The relationship among M, P, and G is illustrated below:

Each M is bound to one kernel thread and one P. The number of P s is set by the GOMAXPROCS environment variable or runtime.GOMAXPROCS(). A fixed number of P s means only that many threads run Go code simultaneously.

When a goroutine performs a blocking system call, its M discards the current P so other goroutines can be scheduled. After the syscall returns, the M attempts to steal a P; if unsuccessful, the goroutine is placed in a global runqueue.

Periodically, each P checks the global runqueue for pending goroutines, ensuring balanced work distribution across processors.

References

The Go scheduler

《Go并发编程第一版》

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

concurrencyGoSchedulerCSPGoroutineChannel
MaGe Linux Operations
Written by

MaGe Linux Operations

Founded in 2009, MaGe Education is a top Chinese high‑end IT training brand. Its graduates earn 12K+ RMB salaries, and the school has trained tens of thousands of students. It offers high‑pay courses in Linux cloud operations, Python full‑stack, automation, data analysis, AI, and Go high‑concurrency architecture. Thanks to quality courses and a solid reputation, it has talent partnerships with numerous internet firms.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.