简析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语言学习笔记、语法知识、技术要点和个人理解及实战

全部评论

相关推荐

会飞的猿:本人来了,手一抖转错了,我是学生,能还给我吗
点赞 评论 收藏
分享
01-07 15:50
四川大学 Java
看日出看日落:好好背八股,做算法。我身边跟你bg差不多的基本都大厂暑期
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客企业服务