并发
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 := <-ch 中 ok==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 传递所有权;必须共享时再加锁,锁粒度尽量小。
小结
go f()启动;用 WaitGroup/channel 等它结束- 无缓冲同步碰头;有缓冲解耦吞吐
select做多路复用与超时- 共享状态加锁,并用
-race验证