golang条件变量

在 Go 语言中,条件变量(Condition Variable)是一种同步机制,用于协调多个 goroutine 之间的执行,一般与互斥锁(sync.Mutex)配合使用。它允许一个或多个 goroutine 在某个条件不满足时暂停执行(等待),并在其他 goroutine 改变状态并通知条件满足时恢复执行。

Go 语言通过 sync.Cond 类型来实现条件变量。

核心概念

sync.Cond 有三个主要方法:

  • Wait(): 使当前 goroutine 释放关联的锁并进入等待状态,直到被 Signal() 或 Broadcast() 唤醒。调用 Wait() 前必须已持有锁。
  • Signal(): 唤醒一个正在等待的 goroutine(如果有的话)。如果有多个等待者,唤醒其中一个。
  • Broadcast(): 唤醒所有正在等待的 goroutine。

基本用法

package main

import (
    "fmt"
    "sync"
    "time"
)

func main() {
    var mu sync.Mutex
    var cond = sync.NewCond(&mu)
    var ready bool

    // 等待者 goroutine
    go func() {
       mu.Lock()    // 获取锁
       for !ready { // 使用 for 循环检查条件(防止虚假唤醒)
          cond.Wait() // 释放锁并等待通知
       }
       fmt.Println("资源已就绪,开始工作...")
       mu.Unlock()
    }()

    // 通知者 goroutine
    go func() {
       time.Sleep(2 * time.Second) // 模拟准备资源
       mu.Lock()
       ready = true
       fmt.Println("资源准备完毕,发出通知")
       cond.Signal() // 或 cond.Broadcast()
       mu.Unlock()
    }()

    // 等待 goroutine 完成(这里简化处理)
    time.Sleep(3 * time.Second)
}

输出:

资源准备完毕,发出通知
资源已就绪,开始工作...

关键要点

  • 必须与锁配合使用:sync.Cond 必须关联一个 sync.Locker(一般是 sync.Mutex 或 sync.RWMutex)。Wait() 会自动释放锁,并在唤醒后重新获取锁。
  • 使用 for 循环检查条件:永远不要用 if 语句检查条件后调用 Wait()。由于存在虚假唤醒(Spurious Wakeup)的可能性,即 goroutine 在没有收到 Signal 的情况下被唤醒。使用 for !condition 可以确保唤醒后条件的确 满足。
  • Signal() vs Broadcast():Signal(): 只唤醒一个等待者。适用于只有一个 goroutine 需要被唤醒的场景(如生产者-消费者模型中的一个消费者)。Broadcast(): 唤醒所有等待者。适用于状态改变可能影响所有等待者的场景。
  • 通知时一般需要持有锁:在调用 Signal() 或 Broadcast() 之前,一般需要持有与 Cond 关联的锁,以确保状态的修改是原子的。

总结

sync.Cond 是 Go 中实现条件等待的底层工具,使用时需注意:

  • 始终与 sync.Mutex 配合。
  • 用 for 循环检查条件。
  • 理解 Signal 和 Broadcast 的区别。
  • 在大多数需要通信的场景下,channel 是更推荐的高级抽象。
© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容