2.3 Cond(条件变量)
在并发编程中,某些情况下我们需要一种机制,使得 goroutine 能够在满足特定条件时进行等待,并在条件满足后被唤醒。Go语言中的 sync.Cond 提供了这种机制,它通过条件变量实现 goroutine 之间的协调。下面我们详细介绍 Cond 的概念、使用方法及示例。
2.3.1 什么是Cond
Cond 是 Go 标准库 sync 包中的一种同步原语,代表条件变量。条件变量使得一个 goroutine 可以等待某个条件满足,而其他 goroutine 可以在条件满足后唤醒它们。Cond 一般与互斥锁 (Mutex) 一起使用,以保证条件检查和条件等待的原子性。
2.3.2 Cond的基本方法
Cond 主要有以下三个方法:
Wait:使当前 goroutine 等待条件变量,直到被唤醒。调用Wait前必须先锁定关联的互斥锁,Wait方法会在等待时解锁互斥锁,并在返回前重新锁定它。Signal:唤醒一个正在等待条件变量的 goroutine。Broadcast:唤醒所有正在等待条件变量的 goroutine。
2.3.3 Cond的使用方法
使用 Cond 需要创建一个 sync.Cond 实例,并传入一个互斥锁。以下是基本使用示例:
package main
import (
"fmt"
"sync"
"time"
)
var (
mu sync.Mutex
cond = sync.NewCond(&mu)
ready = false
)
func waitCondition(id int, wg *sync.WaitGroup) {
defer wg.Done()
cond.L.Lock()
for !ready {
cond.Wait()
}
fmt.Printf("Goroutine %d ready\n", id)
cond.L.Unlock()
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 5; i++ {
wg.Add(1)
go waitCondition(i, &wg)
}
time.Sleep(1 * time.Second)
cond.L.Lock()
ready = true
cond.L.Unlock()
cond.Broadcast()
wg.Wait()
fmt.Println("All goroutines ready")
}
在这个示例中,五个 goroutine 会等待 ready 变量变为 true。当 ready 被设置为 true 后,调用 Broadcast 唤醒所有等待的 goroutine。
2.3.4 Cond的应用场景
Cond 适用于以下场景:
- 生产者-消费者问题:生产者生产数据,消费者消费数据,使用 Cond 可以协调生产者和消费者的操作。
- 线程池管理:在线程池中,工作线程可以等待任务的到来,并在任务到来时被唤醒。
- 事件通知系统:在事件驱动系统中,事件的发生可以通过 Cond 通知等待的 goroutine。
2.3.5 示例代码
以下是一个使用 Cond 实现简单生产者-消费者模型的示例:
package main
import (
"fmt"
"sync"
"time"
)
var (
mu sync.Mutex
cond = sync.NewCond(&mu)
queue []int
maxSize = 5
)
func produce(wg *sync.WaitGroup, id int) {
defer wg.Done()
for i := 0; i < 10; i++ {
cond.L.Lock()
for len(queue) == maxSize {
cond.Wait()
}
queue = append(queue, i)
fmt.Printf("Producer %d produced: %d\n", id, i)
cond.L.Unlock()
cond.Signal()
time.Sleep(100 * time.Millisecond)
}
}
func consume(wg *sync.WaitGroup, id int) {
defer wg.Done()
for i := 0; i < 10; i++ {
cond.L.Lock()
for len(queue) == 0 {
cond.Wait()
}
item := queue[0]
queue = queue[1:]
fmt.Printf("Consumer %d consumed: %d\n", id, item)
cond.L.Unlock()
cond.Signal()
time.Sleep(150 * time.Millisecond)
}
}
func main() {
var wg sync.WaitGroup
for i := 1; i <= 2; i++ {
wg.Add(1)
go produce(&wg, i)
}
for i := 1; i <= 2; i++ {
wg.Add(1)
go consume(&wg, i)
}
wg.Wait()
fmt.Println("All tasks completed")
}
在这个示例中,生产者 goroutine 和消费者 goroutine 使用 Cond 进行协调,以确保生产者不会在队列已满时继续生产,消费者不会在队列为空时继续消费。
结论
Cond 提供了一种高效的机制来协调多个 goroutine 的执行,使得它们能够在满足特定条件时进行等待和被唤醒。通过合理使用 Cond,可以简化并发程序的设计,确保数据一致性和并发性能。在实际应用中,需要根据具体需求选择合适的同步原语,以达到最佳的性能和安全性。在接下来的章节中,我们将继续探讨其他同步原语和并发编程技巧,帮助您更好地掌握 Go 的并发编程。