Golang中sync.Cond需与sync.Mutex结合使用,因Cond仅负责通知,而Mutex保护共享状态。Wait()在条件不满足时释放锁并挂起,被唤醒后重新获取锁,确保安全检查条件。典型应用场景如生产者-消费者模型,通过Signal()唤醒一个等待者或Broadcast()唤醒所有等待者。常见陷阱包括未在循环中检查条件导致虚假唤醒问题,最佳实践是始终用for循环检查条件、封装Cond与锁、按需选择Signal或Broadcast,避免竞态和性能损耗。

Golang中的
sync.Cond
sync.Mutex
要使用
sync.Cond
sync.Mutex
sync.RWMutex
Cond.Wait()
Wait()
Signal()
Broadcast()
Cond.Signal()
Cond.Broadcast()
一个典型的例子是实现一个有限容量的缓冲区:
package main
import (
"fmt"
"sync"
"time"
)
// Buffer 是一个简单的有限容量缓冲区
type Buffer struct {
mu sync.Mutex
cond *sync.Cond
items []int
capacity int
}
// NewBuffer 创建一个新的缓冲区
func NewBuffer(capacity int) *Buffer {
b := &Buffer{
items: make([]int, 0, capacity),
capacity: capacity,
}
b.cond = sync.NewCond(&b.mu) // 将互斥锁传递给条件变量
return b
}
// Put 将一个元素放入缓冲区
func (b *Buffer) Put(item int) {
b.cond.L.Lock() // 获取互斥锁保护共享状态
defer b.cond.L.Unlock()
// 如果缓冲区满了,就等待
for len(b.items) == b.capacity {
fmt.Printf("生产者 %d: 缓冲区已满,等待...\n", item)
b.cond.Wait() // 释放锁并等待,被唤醒后重新获取锁
}
b.items = append(b.items, item)
fmt.Printf("生产者 %d: 放入 %d,当前缓冲区: %v\n", item, item, b.items)
b.cond.Signal() // 通知一个等待的消费者
}
// Get 从缓冲区取出一个元素
func (b *Buffer) Get() int {
b.cond.L.Lock() // 获取互斥锁保护共享状态
defer b.cond.L.Unlock()
// 如果缓冲区为空,就等待
for len(b.items) == 0 {
fmt.Println("消费者: 缓冲区为空,等待...")
b.cond.Wait() // 释放锁并等待,被唤醒后重新获取锁
}
item := b.items[0]
b.items = b.items[1:]
fmt.Printf("消费者: 取出 %d,当前缓冲区: %v\n", item, b.items)
b.cond.Signal() // 通知一个等待的生产者(如果有的话)
return item
}
func main() {
buf := NewBuffer(3) // 容量为3的缓冲区
// 启动多个生产者
for i := 0; i < 5; i++ {
go func(id int) {
time.Sleep(time.Duration(id) * 100 * time.Millisecond) // 错开生产时间
buf.Put(id)
}(i)
}
// 启动多个消费者
for i := 0; i < 5; i++ {
go func() {
time.Sleep(50 * time.Millisecond) // 消费者稍微晚点启动
buf.Get()
}()
}
// 等待一段时间,观察输出
time.Sleep(2 * time.Second)
fmt.Println("主程序退出。")
}在这个例子中,
Put
Get
Signal()
立即学习“go语言免费学习笔记(深入)”;
这是个非常核心的问题,我刚开始接触
sync.Cond
sync.Cond
sync.Mutex
sync.Cond
Wait()
sync.Cond
Wait()
所以,
sync.Mutex
sync.Cond
sync.Cond
Signal()
Broadcast()
Cond.Signal()
Cond.Wait()
Signal()
Signal()
Cond.Broadcast()
Broadcast()
我的经验是,在不确定的时候,很多人会倾向于使用
Broadcast()
Signal()
在使用
sync.Cond
常见陷阱:
未在循环中检查条件: 这是最常见的错误之一。
Wait()
Signal()
if !condition { cond.Wait() }for
for !condition { cond.Wait() }在不持有互斥锁的情况下调用Wait()
Cond.Wait()
cond.L
Wait()
在不持有互斥锁的情况下修改条件变量所依赖的共享状态:
sync.Cond
cond.L
在不持有互斥锁的情况下调用Signal()
Broadcast()
Signal()
Broadcast()
最佳实践:
始终在for
明确条件变量的职责:
sync.Cond
context.Context
sync.Cond
理解Signal()
Broadcast()
Broadcast()
将条件变量及其关联的互斥锁封装起来: 就像我上面提供的
Buffer
sync.Mutex
sync.Cond
考虑超时和取消:
sync.Cond.Wait()
select
time.After
context.WithTimeout
Cond
context.Done()
总之,
sync.Cond
以上就是Golang使用sync.Cond实现条件变量通知的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号