本文主要是介绍Go 标准库源码分析 - sync包的cond,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
sync包的cond实现了一种条件变量,当共享的资源未准备好时,多个goroutine挂起等待,直到一个master goroutine通知事件发生。
一、数据结构
type Cond struct {noCopy noCopy // 实现了Locker接口,使得Cond对象在go vet扫描时能够检测出Cond对象是否被复制L Locker // 实现了Locker接口,通常使用Mutex或RWMutexnotify notifyList // 维护了等待被唤醒的goroutine队列checker copyChecker // 实际上是uintptr对象,保存自身对象地址
}
二、使用方法
1. NewCond
构建一个新的Cond对象,需要传入一个Locker
func NewCond(l Locker) *Cond {return &Cond{L: l}
}
2. Wait
在调用该方法时,需保证已获得了Cond对象的锁,否则会报错
func (c *Cond) Wait() {c.checker.check() // 检查Cond对象是否被复制t := runtime_notifyListAdd(&c.notify) // 将当前goroutine加入等待唤醒队列c.L.Unlock()runtime_notifyListWait(&c.notify, t) // 将当前goroutine挂起,接收到通知时才会被唤醒c.L.Lock()
}
3. Signal
会按顺序唤醒一个等待的goroutine
func (c *Cond) Signal() {c.checker.check()runtime_notifyListNotifyOne(&c.notify)
}
4. Broadcast
会唤醒所有等待的goroutine
func (c *Cond) Broadcast() {c.checker.check()runtime_notifyListNotifyAll(&c.notify)
}
可以看到在Wait、Signal、Broadcast方法中均有调用到私有属性checker的check方法去检查cond对象是否被复制,以下是check方法的代码
func (c *copyChecker) check() {if uintptr(*c) != uintptr(unsafe.Pointer(c)) &&!atomic.CompareAndSwapUintptr((*uintptr)(c), 0, uintptr(unsafe.Pointer(c))) &&uintptr(*c) != uintptr(unsafe.Pointer(c)) {panic("sync.Cond is copied")}
}
- 首先检查当前checker的地址是否等于保存在checker中的地址,因为Cond对象被复制时,checker会被重新分配内存,此时与保存在checker的地址不等
- check方法第一次调用时会将checker对象地址赋值给自身,第二步对checker对象进行原子CAS操作,将当前checker地址赋值给值为空的checker
- 存在一种情况,在CAS操作前,该goroutine被挂起,其他goroutine并发地修改了checker而导致此次CAS操作返回false而panic,因此需要重复操作1
- 若1、2、3皆不满足,则表示该Cond是复制的,抛出panic
这篇关于Go 标准库源码分析 - sync包的cond的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!