- 经典代码示例
package main
import (
"log"
"math/rand"
"sync"
"time"
)
func main() {
c := sync.NewCond(&sync.Mutex{})
var ready int
for i := 0; i < 10; i++ {
go func(i int) {
time.Sleep(time.Duration(rand.Int63n(10)) * time.Second)
// 加锁更改等待条件
c.L.Lock()
ready++
c.L.Unlock()
log.Printf("运动员#%d 已准备就绪\n", i)
// 广播唤醒所有的等待者
c.Broadcast()
}(i)
}
c.L.Lock()
for ready != 10 {
c.Wait()
log.Println("裁判员被唤醒一次")
}
c.L.Unlock()
//所有的运动员是否就绪
log.Println("所有运动员都准备就绪。比赛开始,3,2,1, ......")
}
这段代码演示了如何使用 Go 语言中的 sync.Cond
条件变量来同步并发执行的协程。下面是代码的详细解释:
- 首先,我们创建了一个条件变量
c
,它用来监控一定的条件(在本例中是运动员是否准备好)。条件变量与互斥锁(sync.Mutex{}
)相关联,因为条件往往涉及共享资源的状态(此处为ready
变量)。
c := sync.NewCond(&sync.Mutex{})
var ready int
- 我们开启 10 个协程,每个代表一个运动员。每个协程将随机等待一段时间,模拟运动员准备就绪所需的时间。
for i := 0; i < 10; i++ {
go func(i int) {
time.Sleep(time.Duration(rand.Int63n(10)) * time.Second)
- 当运动员准备好后,协程会锁定与条件变量关联的互斥锁,增加
ready
计数,并通过调用c.Broadcast()
唤醒所有等待该条件的协程(此例中,裁判员正在等待所有运动员准备好)。
// 加锁更改等待条件
c.L.Lock()
ready++
c.L.Unlock()
log.Printf("运动员#%d 已准备就绪\n", i)
// 广播唤醒所有的等待者
c.Broadcast()
- 主协程(裁判员)在开始比赛之前需要确保所有的运动员都已准备就绪。为此,它锁定互斥锁并检查
ready
计数是否等于 10(即所有运动员都已准备好)。如果没有,则调用c.Wait()
进入等待状态。当c.Broadcast()
被某个运动员调用时,裁判员被唤醒,再次检查条件是否满足。
c.L.Lock()
for ready != 10 {
c.Wait()
log.Println("裁判员被唤醒一次")
}
c.L.Unlock()
在调用 c.Wait()
时,互斥锁会被自动释放,以便其他协程可以锁定它来改变条件。当 c.Wait()
返回时,互斥锁会被重新锁定,以便协程可以安全地检查条件。
- 最后,当所有的运动员都准备就绪时(即
ready
等于 10),主协程(裁判员)会解锁互斥锁并声明所有运动员都准备好了,比赛可以开始。
log.Println("所有运动员都准备就绪。比赛开始,3,2,1, ......")
总之,这段代码通过条件变量 sync.Cond
来同步多个协程间的状态,在本例中是等待所有运动员准备就绪。这是一种常用的同步模式,特别适合于多个协程需要等待某个条件达成才能继续执行的场景。