简析Go语言中条件变量sync.Cond
条件变量是同步原语的一种。sync.Cond是传统的条件变量原语概念在Go语言中的实现。一个条件变量可以理解为一个容器,这个容器中存放着一个或一组等待某个条件成立的goroutine。当条件成立时,处于等待状态的goroutine将得到通知并被唤醒继续工作。
下例中,spawn函数中创建了一组goroutine,所有创建的goroutine都将等待ok条件成立,ok为true后,处于等待状态的goroutine将得到通知并被唤醒继续工作。
import ( "fmt" "sync" "time" ) type signal struct{} var ok bool func foo(i int) { fmt.Printf("start do worker %d\n", i) time.Sleep(1 * time.Second) fmt.Printf("worker %d finished\n", i) } func spawn(f func(i int), num int, sig *sync.Cond) <-chan signal { c := make(chan signal) var wg sync.WaitGroup for j := 1; j <= num; j++ { wg.Add(1) go func(j int) { sig.L.Lock() for !ok { sig.Wait() } sig.L.Unlock() fmt.Printf("spawn worker %d\n", j) f(j) wg.Done() }(j) } go func() { wg.Wait() c <- struct{}{} }() return c } func main() { sig := sync.NewCond(&sync.Mutex{}) c := spawn(foo, 5, sig) time.Sleep(5 * time.Second) fmt.Println("start workers") sig.L.Lock() ready = true sig.Broadcast() sig.L.Unlock() <-c fmt.Println("workers done") } > go run .\main.go start workers spawn worker 3 start do worker 3 spawn worker 1 start do worker 1 spawn worker 4 spawn worker 2 spawn worker 5 start do worker 5 start do worker 2 start do worker 4 worker 4 finished worker 2 finished worker 1 finished worker 5 finished worker 3 finished workers done
使用sync.Cond同步资源消耗更小、使用体验更佳。
sync.Cond实例初始化需要一个满足了sync.Locker接口的类型实例,通常使用sync.Mutex。条件变量需要这两个互斥锁来同步临界区,保护作用条件的数据。各个等待条件成立的goroutine在加锁后判断条件是否成立,如果不成立则调用sync.Cond的Wait方法进入等待状态。Wait方法在所有goroutine挂起前会进行Unlock操作。
main goroutine 将ok置为true并调用sync.Cond的Broadcast方法后,各个阻塞状态的goroutine将被唤醒并从wait方法中返回。在Wait方法返回前,Wait方法会再次加锁让goroutine进入临界区,接下来goroutine会再次对条件数据进行判断,如果条件成立,则解锁进入下一步工作,如果条件依然不成立,那么再次调用Wait方法挂起等待。
Go语言基础及实战 文章被收录于专栏
Go语言学习笔记、语法知识、技术要点和个人理解及实战