Skip to content
Skip to content
DocsGo LearningadvancedConcurrency
Chapter 8 of 14·advanced·10 min read

Concurrency

Lập Trình Đồng Thời

Goroutines, channels, select, and sync primitives

Hover or tap any paragraph to see Vietnamese translation

Concurrency in Go

Go was designed from the ground up for concurrency. It provides simple yet powerful tools like goroutines, channels, and the select statement. Unlike most languages, concurrent programming in Go is a core part of the language design.

Goroutines

A goroutine is a lightweight thread managed by the Go runtime. Goroutines are much lighter than OS threads -> a goroutine costs only about 2KB of stack. The Go runtime uses M:N scheduling to map thousands of goroutines onto fewer OS threads. You start a goroutine with the go keyword.

goroutines.go
1package main23import (4	fmt "fmt"5	"time"6)78func main() {9	// Start a goroutine10	go func() {11		fmt.Println("Hello from goroutine")12	}()

Channels

A channel allows goroutines to communicate safely by sending and receiving values. make(chan int) creates an unbuffered channel where send and receive block until both sides are ready. make(chan int, 10) creates a buffered channel with capacity 10. You send with ch -> v and receive with v := -> ch.

channels.go
1package main23import (4	"fmt"5	"time"6)78func main() {9	// Unbuffered channel10	ch := make(chan string)1112	go func() {

Select Statement

Select lets a goroutine wait on multiple channels. It is similar to switch but for channels. Cases are evaluated in any order, and the first one ready executes. A default case runs if no other case is ready (non-blocking).

select.go
1package main23import (4	"fmt"5	"time"6)78func main() {9	ch1 := make(chan string)10	ch2 := make(chan string)1112	go func() {

sync.WaitGroup

WaitGroup synchronizes a group of goroutines. Call wg.Add(n) to set the number of goroutines, wg.Done() to signal completion (usually defer it), and wg.Wait() to block until all goroutines are done. This is cleaner than adding delays with Sleep.

waitgroup.go
1package main23import (4	"fmt"5	"sync"6)78func main() {9	var wg sync.WaitGroup1011	// Fan-out: launch N goroutines12	for i := 1; i <= 3; i++ {

sync.Mutex

Mutex protects shared data from corruption by allowing only one goroutine to access it at a time. Call mu.Lock() before accessing and mu.Unlock() after (or defer Unlock to ensure unlock even if panic). RWMutex allows multiple goroutines to read concurrently but exclusive write.

mutex.go
1package main23import (4	"fmt"5	"sync"6)78type Counter struct {9	mu    sync.Mutex10	value int11}12

Common Patterns

The done channel pattern signals cancellation. Pipeline pattern chains goroutines: producer sends data, transformer processes it, consumer receives the result. Worker pool launches a fixed number of worker goroutines waiting on a jobs channel.

patterns.go
1package main23import (4	"fmt"5)67// Done channel for cancellation8func cancellationExample() {9	done := make(chan bool)10	ch := make(chan int)1112	go func() {

The Race Detector

Go includes a race detector. Run go run -race main.go to find data races. The race detector reports any unsynchronized access to the same memory from different goroutines. Always use the -race flag when testing concurrent code.

race_detector.go
1// main.go2package main34import (5	"fmt"6)78// BAD: Data race - multiple goroutines access x without synchronization9func badExample() {10	var x int11	go func() {12		x = 1  // Write without sync
Warning
Run go test -race ./... to check all tests with the race detector. If a race is detected, the test fails. This is critical for ensuring concurrent code is safe.

Key Takeaways

Điểm Chính

  • Goroutines are lightweight and managed by the Go runtimeGoroutine nhẹ và được quản lý bởi Go runtime
  • Channels enable safe communication between goroutinesChannel cho phép giao tiếp an toàn giữa các goroutine
  • WaitGroup synchronizes goroutine completionWaitGroup đồng bộ hoàn thành của các goroutine
  • Use -race flag to detect data races in testingSử dụng flag -race để phát hiện data race khi testing

Practice

Test your understanding of this chapter

Quiz

How many OS threads does Go use by default for M:N scheduling?

Go sử dụng bao nhiêu OS thread theo mặc định cho M:N scheduling?

Quiz

What is the main difference between an unbuffered and a buffered channel?

Sự khác biệt chính giữa unbuffered channel và buffered channel là gì?

True or False

The defer keyword with Unlock() is optional because Go automatically unlocks mutexes when a goroutine exits.

Từ khóa defer với Unlock() là tùy chọn vì Go tự động unlock mutex khi goroutine kết thúc.

True or False

A buffered channel with capacity 5 will block on send if it already contains 5 values.

Một buffered channel với dung lượng 5 sẽ block khi gửi nếu nó đã chứa 5 giá trị.

Code Challenge

Launch a goroutine that prints a message

Khởi chạy một goroutine in ra một thông báo

 func() {
	fmt.Println("Hello from goroutine")
}()
Built: 6/25/2026, 3:03:36 PM