sync 包
sync 包
mutex 互斥锁
type Mutex struct {
state int32 //互斥锁上锁状态枚举值如下所示
sema uint32 //信号量,向处于Gwaitting的G发送信号
}
const (
mutexLocked = 1 << iota // 1 互斥锁是锁定的
mutexWoken // 2 唤醒锁
mutexWaiterShift = iota // 2 统计阻塞在这个互斥锁上的goroutine数目需要移位的数值
)互斥锁无冲突是最简单的情况了,有冲突时,首先进行自旋,因为大多数的Mutex保护的代码段都很短,经过短暂的自旋就可以获得;如果自旋等待无果,就只好通过信号量来让当前 goroutine 进入 Gwaitting 状态
once 实现
type Once struct {
m Mutex
done uint32
}
// 实现了双重检测机制保证前期快速返回而且 f 没有执行完的时候不会被重复执行
func (o *Once) Do(f func()) {
if atomic.LoadUint32(&o.done) == 1 {
return
}
// Slow-path.
o.m.Lock()
defer o.m.Unlock()
if o.done == 0 {
defer atomic.StoreUint32(&o.done, 1)
f()
}
}Cond 条件变量
使用 sync.NewCond(l locker) 创建条件变量,参数传入互斥量或者读写锁都可以,返回 *sync.Cond 类型的对象
有三个方法:Wait,Signal,Broadcast 分别对应等待通知、单发通知以及广播通知的操作。 特殊场景需要惊群(全部唤醒)可以使用条件变量
Wait 可以让当前已锁定的 G 暂时先放弃读锁,并阻塞等待通知,此时写 G 可以进行写入,完成后再调用 signal 唤醒 G 继续读
与读写锁搭配时需要注意:
- 一定要事先锁定与之关联的读锁后再调用 Wait 方法
- 读完之后一定也要及时解除关联的锁
与 Wait 方法不同的是,调用 Signal 和 Broadcast 时无须锁定关联的锁
原子操作
常用于无锁编程中,修改时写时拷贝也是一种无锁化方案 让 uint 类型减少 N ?
// 利用二进制补码的特性绕过编译器检查
atomic.AddUint32(&ui32, ^uint32(-N-1))CAS操作
有时我们需要在 for 循环里不停地进行 CAS 操作直到修改完成返回值为 true 才退出循环