模块 7 · 第 4 课(共 5)课程中的34/50~14 min
模块课程(4/5)
`sync.Mutex` 和 `sync.WaitGroup`
通道是 Go 的主要习惯用法,但有时更传统
需要原语。 sync 包提供互斥体,等等
组和其他实用程序来协调共享状态。
sync.Mutex:排它锁
Go
import "sync"
type Counter struct {
mu sync.Mutex
count int
}
func (c *Counter) Inc() {
c.mu.Lock()
defer c.mu.Unlock()
c.count++
}
func (c *Counter) Get() int {
c.mu.Lock()
defer c.mu.Unlock()
return c.count
}一次只有 一个 goroutine 进入 Lock() 和 Unlock() 之间。
强制模式:defer mu.Unlock() 位于 Lock() 之后。
sync.RWMutex:多个读者,一名作者
对于读重状态:
Go
var mu sync.RWMutex
mu.RLock() // read lock: più goroutine simultanee OK
v := data
mu.RUnlock()
mu.Lock() // write lock: esclusivo
data = newValue
mu.Unlock()仅当读取确实压倒写入时才有用;否则是常规的
由于内部较低,sync.Mutex 更简单并且通常更快
争论。
sync.WaitGroup:等待 N 个 goroutine
Go
var wg sync.WaitGroup
for i := 0; i < 3; i++ {
wg.Add(1)
go func(id int) {
defer wg.Done()
work(id)
}(i)
}
wg.Wait() // blocca finché tutti i Done() sono arrivati工作流程:
wg.Add(N)将计数器增加 N(在启动 goroutine 之前)。- 每个 goroutine 在完成时都会调用
wg.Done()(最好是通过defer)。 wg.Wait()会阻塞,直到计数器返回到 0。
sync.Once:线程安全的延迟初始化
Go
var (
once sync.Once
config *Config
)
func GetConfig() *Config {
once.Do(func() {
config = loadConfig()
})
return config
}即使在并发情况下,once.Do 中的回调也仅运行一次
来电。单例/惰性初始化模式。
互斥量与通道:何时使用什么
指南(也来自官方 Go FAQ):
| 案例 | 首选工具 |
|---|---|
| 传递数据所有权 | 频道 |
| 分配工作(工作队列) | 频道 |
| 协调独立的 goroutine | 频道 |
| 保护结构体字段 | 互斥体 |
| 缓存/计数器 | 互斥或原子 |
| 单例参考 | sync.Once |
“编排通道,共享数据互斥”是一个很好的经验法则。
种族检测器
使用 -race 运行测试以发现数据争用:
Bash
go test -race ./...
go run -race main.go竞争检测器检测二进制文件并记录每个不同步的 访问共享内存。它是验证并发代码的工具 #1 生产前。
尝试一下
锻炼#go.m7.l4.e1
尝试:0加载中...
使用 mu.Lock() + defer mu.Unlock() 保护计数的增量。
正在加载编辑器...
显示提示
在 Lock() 之后延迟 mu.Unlock() 保证即使在恐慌时也能释放。
3 次尝试后可用的解决方案
锻炼#go.m7.l4.e2
尝试:0加载中...
启动 3 个 goroutine 并使用sync.WaitGroup 等待所有它们(每个 goroutine 中的 Add(3)、Done()、Wait())。
正在加载编辑器...
显示提示
在启动 goroutine 之前添加;在 goroutine 内部使用 defer 完成。
3 次尝试后可用的解决方案
Quiz#go.m7.l4.e3
准备好保证互斥体被释放的推荐模式是什么?
Go
mu.Lock()
// ?回顾
sync.Mutex保护共享状态;始终defer mu.Unlock()。- 使用指针接收器以避免复制互斥体。
sync.RWMutex适用于多读者/单作者模式(仅当有帮助时)。sync.WaitGroup:Add在go、Done之前,并在defer、Wait等待。sync.Once:线程安全的延迟初始化。 -“编排通道、共享数据互斥”。go test -race:用于发现数据争用的工具#1。