您好,登錄后才能下訂單哦!
這篇文章主要介紹“如何理解Go Cond”,在日常操作中,相信很多人在如何理解Go Cond問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”如何理解Go Cond”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
比如下面這段代碼:
package mainimport ( "fmt" "time")func main() { done := make(chan int, 1) go func() { time.Sleep(5 * time.Second) done <- 1 }() fmt.Println("waiting") <-done fmt.Println("done") }
同樣可以使用 sync.Cond 來實現
package mainimport ( "fmt" "sync" "time")func main() { cond := sync.NewCond(&sync.Mutex{}) var flag bool go func() { time.Sleep(time.Second * 5) cond.L.Lock() flag = true cond.Signal() cond.L.Unlock() }() fmt.Println("waiting") cond.L.Lock() for !flag { cond.Wait() } cond.L.Unlock() fmt.Println("done") }
大部分場景下使用 channel 是比 sync.Cond方便的。不過我們要注意到,sync.Cond 提供了 Broadcast 方法,可以通知所有的等待者。想利用 channel 實現這個方法還是不容易的。我想這應該是 sync.Cond 唯一有用武之地的地方。
先列出來一些問題吧,可以帶著這些問題來閱讀本文:
cond.Wait本身就是阻塞狀態,為什么 cond.Wait 需要在循環內 ?
sync.Cond 如何觸發不能復制的 panic ?
為什么 sync.Cond 不能被復制 ?
cond.Signal 是如何通知一個等待的 goroutine ?
cond.Broadcast 是如何通知等待的 goroutine 的?
sync.cond wait
sync.Cond Signal
sync.Cond Broadcast
sync.Cond 排隊動圖
是阻塞的。不過不是 sleep 這樣阻塞的。
調用 goparkunlock
解除當前 goroutine 的 m 的綁定關系,將當前 goroutine 狀態機切換為等待狀態。等待后續 goready 函數時候能夠恢復現場。
判斷是否有沒有被喚醒的 goroutine,如果都已經喚醒了,直接就返回了
將已通知 goroutine 的數量加1
從等待喚醒的 goroutine 隊列中,獲取 head 指針指向的 goroutine,將其重新加入調度
被阻塞的 goroutine 可以繼續執行
判斷是否有沒有被喚醒的 goroutine,如果都已經喚醒了,直接就返回了
將等待通知的 goroutine 數量和已經通知過的 goroutine 數量設置成相等
遍歷等待喚醒的 goroutine 隊列,將所有的等待的 goroutine 都重新加入調度
所有被阻塞的 goroutine 可以繼續執行
我們能注意到,調用 cond.Wait 的位置,使用的是 for 的方式來調用 wait 函數,而不是使用 if 語句。
這是由于 wait 函數被喚醒時,存在虛假喚醒等情況,導致喚醒后發現,條件依舊不成立。因此需要使用 for 語句來循環地進行等待,直到條件成立為止。
func (c *Cond) Wait() { c.checker.check() t := runtime_notifyListAdd(&c.notify) c.L.Unlock() runtime_notifyListWait(&c.notify, t) c.L.Lock() }
我們看到 Wait 內部會先調用 c.L.Unlock(),來先釋放鎖。如果調用方不先加鎖的話,會觸發“fatal error: sync: unlock of unlocked mutex”。關于 mutex 的使用方法,推薦閱讀下《這可能是最容易理解的 Go Mutex 源碼剖析》
sync.Cond 不能被復制的原因,并不是因為 sync.Cond 內部嵌套了 Locker。因為 NewCond 時傳入的 Mutex/RWMutex 指針,對于 Mutex 指針復制是沒有問題的。
主要原因是 sync.Cond 內部是維護著一個 notifyList。如果這個隊列被復制的話,那么就在并發場景下導致不同 goroutine 之間操作的 notifyList.wait、notifyList.notify 并不是同一個,這會導致出現有些 goroutine 會一直堵塞。
這里留下一個問題,sync.Cond 內部是有一段代碼 check sync.Cond 是不能被復制的,下面這段代碼能觸發這個 panic 嗎?
package mainimport ( "fmt" "sync")func main() { cond1 := sync.NewCond(new(sync.Mutex)) cond := *cond1 fmt.Println(cond) }
有興趣的可以動手嘗試下,以及嘗試下如何才能觸發這個panic "sync.Cond is copied” 。
到此,關于“如何理解Go Cond”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。