相互等待 - WaitGroup
一个(组)协程需要等待另一组协程完成
type WaitGroup struct {
noCopy noCopy // 特殊的结构体, 表示该结构体不能被拷贝
state1 [3]uint32 // counter, waiter counter, sema
}
Wait()
- 如果被等待的协程没了, 直接返回
- 否则, waiter + 1, 陷入sema
Done()
- 被等待协程做完, 给counter - 1
- 通过
wg.Add(-1)实现
Add()
- add counter
- 被等待协程没做完, 或者没人在等待, 返回
- 被等待协程都做完, 且有人等待, 唤醒所有sema中的协程
总结
- WaitGroup:实现了一组协程等待另一组协程等待的协程
- 陷入sema并记录个数
- 被等待的协程计数归零时,唤醒所有sema中的协程
Once
整个程序运行过程中, 代码只执行一次 用来进行一些初始化的操作
// 使用
once := sync.Once{}
go once.Do(func(){})
// 实现
type Once struct {
done uint32
m Mutex
}
func (o *Once) doSlow(f func()) {
o.m.Lock()
defer o.m.Unlock()
if o.done == 0 {
defer atomic.StoreUint32(&o.done, 1)
f()
}
}
- 先判断是否已经改值
- 没改, 尝试获取锁
- 获取到锁的协程执行业务, 改值, 解锁
- 冲突协程唤醒后直接返回
总结
- sync.Once实现了一段代码只执行一次
- 使用标志 + mutex:实现了并发冲突的优化
锁常见问题
锁拷贝
- 锁拷贝可能导致锁的死锁问题
- 使用vet工具可以检测锁拷贝问题
- vet还能检测可能的bug或者可疑的构造
RACE竞争检测
- 发现隐含的数据竞争问题
- 可能是加锁的建议
- 可能是bug的提醒
go-deadlock检测
- 检测可能的死锁
- 实际是检测获取锁的等待时间
- 用来排查bug和性能问题