前言
在 Go 裡有專門為同步通信而生的
channel,是以較少看到 sync.Cond 的使用。不過它也是并發控制手段裡的一種,今天我們就來認識下它的相關實作,加深對同步機制的運用。
sync.Cond
sync.Cond 提供了三個方法:Wait()、Signal()、Broadcast(),它們的用法如下:
- Wait():阻塞目前的 goroutine,等待喚起。
- Signal():喚起一個阻塞的 goroutine。
- Broadcast():喚起所有阻塞的 goroutine。
通過上面的方法描述,我們就可以簡單的實作一個任務池功能:先批量的建立 goroutine,然後調用 sync.Cond 的 Wait() 方法讓其阻塞的等待。
當有一個任務到來時,則通過 Signal() 喚起剛剛在阻塞的某一個 goroutine,去執行任務。
通過任務池功能,我們發現 sync.Cond 的運用很簡單,但 Go 官方并不推薦我們使用 sync.Cond 來實作協程間的同步通信。
因為它并不符合 Go 官方 “通過通信來共享記憶體” 的設計思想,當場景複雜時,則會耦合各個業務功能。
sync.Cond 源碼分析
我們來看下 sync.Cond 的結構體,代碼在 /sr/sync/cond.go 下:
type Cond struct {
noCopy noCopy // 不可複制
L Locker // 鎖
notify notifyList // 通知喚起清單
checker copyChecker // 複制檢測
}
可以看到 Cond 上有 notify 清單,而這正是維護了需要喚起的 goroutine 清單。
當我們調用 Wait() 方法的時候就會維護目前 goroutine 到對應的 notifyList 裡:
func (c *Cond) Wait() {
c.checker.check()
t := runtime_notifyListAdd(&c.notify) // 将目前 goroutine 添加到 notifyList 裡
c.L.Unlock()
runtime_notifyListWait(&c.notify, t) // 阻塞等待
c.L.Lock()
}
當有其他協程調用了 Signal 或 Broadcast 方法時,則會通過
runtime_notifyListNotifyOne
或
runtime_notifyListNotifyAll
方法來喚起一個或多個 goroutine。
其他同步方式的實作
前面提到到 sync.Cond 并不被推薦作為協同通信手段,那如果要實作它的單點傳播、廣播效果,該怎麼弄呢?
其實也很簡單,如果我們要實作單點傳播效果,那麼隻需要通過阻塞的監聽 channel 信号即可。
如果要實作廣播喚起效果,隻需要利用
context的鍊式取消特性,也能達到該效果。