
Go (Golang) cung cấp mô hình đồng bộ hóa đồng thời (concurrency) dựa trên CSP (Communicating Sequential Processes), cho phép lập trình viên viết chương trình đa nhiệm hiệu quả mà không cần lo lắng về race conditions phức tạp. Hai trụ cột chính của Go concurrency là goroutine — luồng nhẹ do runtime quản lý, và channel — phương thức truyền dữ liệu an toàn giữa các goroutine.
Goroutine: Luồng Nhẹ Với Chi Phí Thấp
Khác với thread của OS (mặc định 2MB stack), goroutine khởi đầu chỉ 2KB và tự động mở rộng. Điều này cho phép tạo hàng trăm nghìn goroutine mà không ảnh hưởng bộ nhớ. Go runtime scheduler phân phối goroutine trên OS thread một cách thông minh, sử dụng M:N scheduling — nhiều goroutine ánh xạ vào ít thread.
- Khởi tạo dễ dàng:
go func()— một dòng lệnh tạo goroutine mới - Lightweight: ~2KB ban đầu, grow/shrink tự động
- Scheduler: Work-stealing algorithm, không cần quản lý thread thủ công
- Không shared memory: Khuyến khích giao tiếp qua channel thay vì lock

Channel: Giao Tiếp An Toàn Giữa Các Goroutine
Channel là cách idiom của Go để truyền dữ liệu giữa goroutine. Channel có thể unidirectional (chỉ gửi hoặc chỉ nhận) hoặc bidirectional. Buffer không đồng bộ (unbuffered) đồng bộ hóa cả hai bên, trong khi buffered cho phép gửi/nhận không đồng bộ đến khi đầy/rỗng.
Nguyên tắc cốt lõi của Go: “Don’t communicate by sharing memory; share memory by communicating”. Thay vì mutex lock bảo vệ shared state, Go khuyến khích mỗi goroutine sở hữu dữ liệu riêng và truyền qua channel.
Các Pattern Đồng Thời Phổ Biến
Worker Pool
Worker Pool giới hạn số goroutine đồng thời xử lý task, tránh tạo quá nhiều goroutine. Phù hợp cho xử lý batch, image processing, web scraping.

Fan-out / Fan-in
Fan-out: một channel chia dữ liệu thành nhiều consumer goroutine. Fan-in: nhiều channel gộp vào một. Kết hợp cả hai tạo pipeline xử lý song song hiệu quả cao.
// Fan-out: 3 worker đọc từ job channel
for i := 0; i < 3; i++ {
go func(id int) {
for job := range jobs {
process(job)
}
}(i)
}
// Fan-in: gộp kết quả từ nhiều channel
results := merge(out1, out2, out3)
Context: Hủy Bỏ, Timeout, Deadline
Package context là chuẩn truyền tín hiệu hủy bỏ (cancellation) qua call stack. Khi parent context bị hủy, tất cả goroutine con nhận được signal và dừng lại. Context cũng hỗ trợ timeout và deadline — thiết yếu cho API server.
| Hàm | Mục đích | Ví dụ |
|---|---|---|
context.Background() |
Root context, không hủy | Main function |
context.WithTimeout() |
Hủy sau thời gian cố định | HTTP request 5s timeout |
context.WithCancel() |
Hủy thủ công | User nhấn Ctrl+C |
context.WithValue() |
Lưu trace ID, auth | Request-scoped values |
Xử Lý Lỗi Và Race Condition
Go phát hiện race condition runtime qua -race flag. Tuy nhiên, cách tốt nhất là thiết kế tránh shared state. Khi cần mutex, sync.Mutex và sync.RWMutex cung cấp bảo vệ.
- sync.WaitGroup: Chờ nhiều goroutine hoàn thành
- sync.Once: Đảm bảo hàm chạy một lần duy nhất
- sync.Cond: Biến điều kiện cho goroutine chờ
- errgroup.Group: Quản lý nhóm goroutine với error propagation
Ứng dụng thực tế
Go concurrency được sử dụng rộng rãi trong:
- Web server: HTTP server mặc định xử lý mỗi request một goroutine (net/http)
- Microservices: gRPC client/server với stream concurrency
- Data pipeline: ETL, Kafka consumer group, real-time processing
- CLI tools: Parallel download, concurrent file operations
So sánh với các ngôn ngữ khác
Go concurrency đơn giản hơn Java Thread (không cần ExecutorService boilerplate) và Erlang/Elixir actor model (không cần OTP framework). Tuy nhiên Go không có preemption — goroutine có thể chạy mãi nếu không yield. Go 1.14+ cải thiện preemption qua asynchronous preemption.
Tóm lại: Go concurrency không chỉ là công cụ — nó là cách tư duy lập trình. Thiết kế đúng giúp chương trình Go mở rộng ngang (horizontal scale) tự nhiên mà không phức tạp. Bắt đầu với go run -race, dùng context, channel là nền tảng vững chắc.
Tham khảo thêm: Go Blog: Pipelines and cancellation | Go by Example
