Skip to main content

并发

goroutine 与 channel 是 Go 并发的核心心智模型。

goroutine

go 关键字启动轻量并发单元:

func main() {
go func() {
fmt.Println("async")
}()
time.Sleep(10 * time.Millisecond) // 演示用;生产用同步原语/channel
}
  • 不要假设执行顺序
  • main 返回则进程结束,未跑完的 goroutine 会被干掉
  • 用 WaitGroup / channel / context 做生命周期管理,而不是盲目 Sleep
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
fmt.Println("work")
}()
wg.Wait()

channel

channel 用于 goroutine 间通信(「用通信共享内存」):

ch := make(chan int) // 无缓冲
go func() { ch <- 1 }() // 发送
v := <-ch // 接收
fmt.Println(v)

buf := make(chan string, 2) // 有缓冲
buf <- "a"
buf <- "b"
close(buf)
for s := range buf {
fmt.Println(s)
}
类型行为直觉
无缓冲发送与接收必须同时就绪(同步碰头)
有缓冲缓冲区未满可先发;未空可先收
close接收方 v, ok := <-chok==false 表示已关闭且读完;只应由发送方关闭

向已关闭 channel 发送会 panic;重复 close 也会 panic。

select

同时等待多个 channel 操作:

select {
case v := <-ch1:
fmt.Println("ch1", v)
case ch2 <- 1:
fmt.Println("sent")
case <-time.After(100 * time.Millisecond):
fmt.Println("timeout")
default:
fmt.Println("no communication ready")
}

多个 case 都就绪时,Go 随机选一个。default 使 select 非阻塞。

同步原语与竞态

共享内存时仍可用 sync.Mutex / RWMutex

var (
mu sync.Mutex
n int
)

func inc() {
mu.Lock()
defer mu.Unlock()
n++
}

数据竞争(data race)是未同步的并发读写。排查:

go test -race ./...

细节见「测试与工具」。原则:优先 channel 传递所有权;必须共享时再加锁,锁粒度尽量小。

小结

  1. go f() 启动;用 WaitGroup/channel 等它结束
  2. 无缓冲同步碰头;有缓冲解耦吞吐
  3. select 做多路复用与超时
  4. 共享状态加锁,并用 -race 验证

上一篇:接口与组合 · 下一篇:错误处理