Go Concurrency: Goroutines, Channels & Context
Goroutines vs OS threads, channels and select, WaitGroup and errgroup, mutex and sync.Map, context cancellation and timeouts — production Go concurrency.
Goroutines
A goroutine is a lightweight user-space thread managed by the Go runtime: initial stack is only 2KB, and the multiplexes M goroutines onto N OS threads. Creating a goroutine is nearly free — go f() — and millions can exist simultaneously.
go func(msg string) {
fmt.Println(msg)
}("async hello")
// 注意:main 退出后所有 goroutine 立即终止
// 需要同步机制等待它们完成(见 WaitGroup)
Channels and select
// 无缓冲通道:同步握手
ch := make(chan int)
// 有缓冲通道:异步直到满
buf := make(chan int, 10)
buf <- 1 // 发送(缓冲满时阻塞)
v := <-buf // 接收(缓冲空时阻塞)
close(buf) // 关闭,之后接收立即得到零值
// range 遍历直到关闭
for n := range buf {
fmt.Println(n)
}
// select:多路复用
select {
case v := <-ch:
fmt.Println("got", v)
case <-time.After(2 * time.Second):
fmt.Println("timeout")
default:
fmt.Println("no value ready")
}
sync: WaitGroup, Mutex, errgroup
import "sync"
var (
mu sync.Mutex
count int
)
func inc() {
mu.Lock()
count++
mu.Unlock()
}
// WaitGroup:等待一组 goroutine
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
inc()
}(i)
}
wg.Wait()
// errgroup:带错误传播的 WaitGroup(golang.org/x/sync)
g, ctx := errgroup.WithContext(context.Background())
for _, url := range urls {
url := url
g.Go(func() error { return fetch(ctx, url) })
}
if err := g.Wait(); err != nil { /* 第一个错误 */ }
Context: Cancellation and Timeouts
context.Context is the standard way to carry deadlines, cancellation signals and request-scoped values across API boundaries. Every blocking call in a server should accept a context.
func handler(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 3*time.Second)
defer cancel()
select {
case <-ctx.Done():
w.WriteHeader(504) // 超时/取消
return
case result := <-work(ctx):
json.NewEncoder(w).Encode(result)
}
}
// context.Background():根 context,main / 初始化用
// context.TODO():还没想好时占位
- context.WithCancel / WithTimeout / WithValue derive from a parent; canceling a parent cancels all children.
- Pass ctx as the first argument, never store it in a struct field.
goroutine
goroutine 是 Go 运行时管理的轻量级用户态线程:初始栈仅 2KB,由运行时把 M 个 goroutine 复用到 N 个操作系统线程上。创建 goroutine 几乎零成本——go f()——同时存在上百万个也不稀奇。
go func(msg string) {
fmt.Println(msg)
}("async hello")
// 注意:main 退出后所有 goroutine 立即终止
// 需要同步机制等待它们完成(见 WaitGroup)
channel 与 select
// 无缓冲通道:同步握手
ch := make(chan int)
// 有缓冲通道:异步直到满
buf := make(chan int, 10)
buf <- 1 // 发送(缓冲满时阻塞)
v := <-buf // 接收(缓冲空时阻塞)
close(buf) // 关闭,之后接收立即得到零值
// range 遍历直到关闭
for n := range buf {
fmt.Println(n)
}
// select:多路复用
select {
case v := <-ch:
fmt.Println("got", v)
case <-time.After(2 * time.Second):
fmt.Println("timeout")
default:
fmt.Println("no value ready")
}
sync 包:WaitGroup、Mutex、errgroup
import "sync"
var (
mu sync.Mutex
count int
)
func inc() {
mu.Lock()
count++
mu.Unlock()
}
// WaitGroup:等待一组 goroutine
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
inc()
}(i)
}
wg.Wait()
// errgroup:带错误传播的 WaitGroup(golang.org/x/sync)
g, ctx := errgroup.WithContext(context.Background())
for _, url := range urls {
url := url
g.Go(func() error { return fetch(ctx, url) })
}
if err := g.Wait(); err != nil { /* 第一个错误 */ }
context:取消与超时
context.Context 是跨 API 边界传递截止时间、取消信号与请求级值的标准方式。服务端每个阻塞调用都应接收 context。
func handler(w http.ResponseWriter, r *http.Request) {
ctx, cancel := context.WithTimeout(r.Context(), 3*time.Second)
defer cancel()
select {
case <-ctx.Done():
w.WriteHeader(504) // 超时/取消
return
case result := <-work(ctx):
json.NewEncoder(w).Encode(result)
}
}
// context.Background():根 context,main / 初始化用
// context.TODO():还没想好时占位
- WithCancel / WithTimeout / WithValue 从父 context 派生,取消父会级联取消所有子。
- 把 ctx 作为函数第一个参数传递,不要存进结构体字段。