前言
go中的sync.Cond也就是condition兰迫,是一個(gè)條件同步變量辈双,與Java中Object的wait聚请、notify蹄衷、notifyAll方法或者Condition類的作用比較類似忧额,如果有這方面的基礎(chǔ)學(xué)習(xí)起來(lái)會(huì)非常簡(jiǎn)單。其實(shí)Java中的JUC包實(shí)現(xiàn)的可以是最豐富和易用的了愧口,熟知JUC的話睦番,學(xué)習(xí)其他語(yǔ)言的并發(fā)特性及工具的話會(huì)非常簡(jiǎn)單。
語(yǔ)法基礎(chǔ)
sync.Cond同其他并發(fā)條件變量一樣耍属,提供了阻塞和喚醒函數(shù):
Wait() 阻塞操作
Signal() 喚醒一個(gè)協(xié)程
Broadcast() 喚醒所有協(xié)程
不同的Cond需要我們制定一把鎖托嚣,通常是Mutex、RWMytex厚骗,當(dāng)然也可以是你自己實(shí)現(xiàn)的鎖示启。
下面來(lái)看一下sync.Cond的使用:
func main() {
lock := &sync.Mutex{}
cond := sync.NewCond(lock)
for i:=0; i<10; i++ {
runGorotine(cond, i)
}
time.Sleep(1*time.Millisecond)
fmt.Println("----------------------------: signal 喚醒單個(gè)")
cond.Signal()
time.Sleep(1*time.Millisecond)
fmt.Println("----------------------------: broadcast 喚醒全部")
cond.Broadcast()
time.Sleep(2*time.Second)
}
func runGorotine(cond *sync.Cond, i int) {
go func(cond *sync.Cond, i int) {
cond.L.Lock()
for condition() {
fmt.Println("-goroutine-" + strconv.Itoa(i) + " 命中wait")
cond.Wait()
}
fmt.Println("-goroutine-" + strconv.Itoa(i) + " 命中條件")
cond.L.Unlock()
}(cond, i)
}
func condition() bool {
rand.Intn(50)
if rand.Intn(50) > 20 {
fmt.Print(true)
return true
}
fmt.Print(false)
return false
}
輸出:
ps:go 協(xié)程之后啟動(dòng)后并不是立即執(zhí)行的,需要有一定的分配過(guò)程及等待的時(shí)間,所以說(shuō)sleep一小段時(shí)間领舰,否則喚醒通知在wait之前發(fā)生就沒(méi)有意義了夫嗓。
上述就是Cond的最簡(jiǎn)單的使用,生產(chǎn)環(huán)境比這個(gè)demo要復(fù)雜一些冲秽,但是大致也就這樣了舍咖。
實(shí)現(xiàn)原理
Cond的實(shí)現(xiàn)非常簡(jiǎn)單,鎖操作依賴的是我們創(chuàng)建的lock锉桑。然后依賴于runtime阻塞和喚醒go協(xié)程的函數(shù)排霉。
type Cond struct {
// 這個(gè)已經(jīng)不是第一次見(jiàn)了,第一次使用后就不能copy了
noCopy noCopy
L Locker // 創(chuàng)建cond是傳入的鎖
notify notifyList // 調(diào)用runtime阻塞和喚醒的函數(shù)民轴,內(nèi)部維護(hù)了一個(gè)阻塞鏈表
checker copyChecker // 保留指向自身的指針以檢測(cè)對(duì)象復(fù)制攻柠。
}
// 構(gòu)造函數(shù)
func NewCond(l Locker) *Cond {
return &Cond{L: l}
}
wait函數(shù)的鎖邏輯有點(diǎn)奇怪球订,其實(shí)主要是為了把外面條件判斷和添加阻塞隊(duì)列變?yōu)橐粋€(gè)原子操作,這種鎖的使用方式其實(shí)不太建議瑰钮,比較容易出問(wèn)題冒滩。
func (c *Cond) Wait() {
c.checker.check()
t := runtime_notifyListAdd(&c.notify) // 添加阻塞列表
c.L.Unlock() // 函數(shù)調(diào)用前已經(jīng)加鎖了,在添加阻塞隊(duì)列后就可以撤銷鎖了
runtime_notifyListWait(&c.notify, t) // 進(jìn)行阻塞
c.L.Lock() // 外層還有個(gè)解鎖操作浪谴,加把鎖
}
下面是喚醒操作:
func (c *Cond) Signal() {
c.checker.check()
runtime_notifyListNotifyOne(&c.notify)
// 喚醒一個(gè)
}
func (c *Cond) Broadcast() {
c.checker.check()
runtime_notifyListNotifyAll(&c.notify)
// 喚醒多個(gè)
}
關(guān)于Cond的使用及源碼實(shí)現(xiàn)暫時(shí)介紹這么多旦部。