Go sync-Cond:最容易被忽視的同步機制

引言

在探討 Go 語言中的同步機制時,大多數開發者都熟悉 sync.Mutex 和 sync.RWMutex。然而,還有一個強大但經常被忽視的同步原語:sync.Cond。本文將詳細介紹 sync.Cond,解釋它的工作原理,並通過實際示例說明其應用場景。

什麼是 sync.Cond?

當一個 goroutine 需要等待一些特定的事情發生時,例如一些共享數據發生變化,它就會 "阻塞",這意味着它只是暫停工作,直到獲得繼續工作的許可。最基本的方法是使用循環,甚至可以添加 time.Sleep,以防止 CPU 忙碌等待而發瘋。

sync.Cond 是一個條件變量,它提供了一種等待和通知 goroutine 的機制。它在需要多個 goroutine 基於某個條件進行協調的場景中特別有用。

基本語法如下:

var mu sync.Mutex
cond := sync.NewCond(&mu)
sync.Cond 主要提供三個方法:

實際示例

讓我們通過一個隊列處理的例子來理解 sync.Cond 的用法:

type Queue struct {
    cond *sync.Cond
    data []interface{}
    capacity int
}

func NewQueue(capacity int) *Queue {
    return &Queue{
        cond: sync.NewCond(&sync.Mutex{}),
        capacity: capacity,
    }
}

func (q *Queue) Put(item interface{}) {
    q.cond.L.Lock()
    defer q.cond.L.Unlock()
    
    // 當隊列已滿時等待
    for len(q.data) == q.capacity {
        q.cond.Wait()
    }
    
    q.data = append(q.data, item)
    // 通知等待中的消費者
    q.cond.Signal()
}

func (q *Queue) Get() interface{} {
    q.cond.L.Lock()
    defer q.cond.L.Unlock()
    
    // 當隊列爲空時等待
    for len(q.data) == 0 {
        q.cond.Wait()
    }
    
    item := q.data[0]
    q.data = q.data[1:]
    // 通知等待中的生產者
    q.cond.Signal()
    return item
}

sync.Cond 的關鍵特性

  1. 避免虛假喚醒:
for !condition() {
    cond.Wait()
}

使用 for 循環而不是 if 語句來檢查條件,這樣可以防止虛假喚醒。

  1. 必須持有鎖:

調用 Wait() 前必須持有鎖 Wait() 會自動釋放鎖,並在被喚醒時重新獲取鎖 Signal vs Broadcast:Signal() 只喚醒一個等待的 goroutine Broadcast() 喚醒所有等待的 goroutine 適用場景

示例:等待特定條件

type Server struct {
    cond      *sync.Cond
    ready     bool
}

func NewServer() *Server {
    return &Server{
        cond: sync.NewCond(&sync.Mutex{}),
    }
}

func (s *Server) WaitForReady() {
    s.cond.L.Lock()
    defer s.cond.L.Unlock()
    
    for !s.ready {
        s.cond.Wait()
    }
}

func (s *Server) SetReady() {
    s.cond.L.Lock()
    s.ready = true
    s.cond.L.Unlock()
    s.cond.Broadcast()
}

最佳實踐

  1. 始終使用 for 循環進行條件檢查

  2. 確保正確的鎖定 / 解鎖操作

  3. 適當選擇 Signal() 或 Broadcast()

  4. 避免在持有鎖時執行耗時操作

性能考慮 - sync.Cond 在以下情況下特別高效:

結論

sync.Cond 是 Go 語言中一個強大但常被忽視的同步工具。它在需要基於條件協調多個 goroutine 的場景中特別有用。通過合理使用 sync.Cond,我們可以實現更高效和優雅的併發控制。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/3q7Jt4Yh4-VE28jH2UF1jg