本文主要是介绍GO学习之 条件变量 sync.Cond,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
GO系列
1、GO学习之Hello World
2、GO学习之入门语法
3、GO学习之切片操作
4、GO学习之 Map 操作
5、GO学习之 结构体 操作
6、GO学习之 通道(Channel)
7、GO学习之 多线程(goroutine)
8、GO学习之 函数(Function)
9、GO学习之 接口(Interface)
10、GO学习之 网络通信(Net/Http)
11、GO学习之 微框架(Gin)
12、GO学习之 数据库(mysql)
13、GO学习之 数据库(Redis)
14、GO学习之 搜索引擎(ElasticSearch)
15、GO学习之 消息队列(Kafka)
16、GO学习之 远程过程调用(RPC)
17、GO学习之 goroutine的调度原理
18、GO学习之 通道(nil Channel妙用)
19、GO学习之 同步操作sync包
20、GO学习之 互斥锁、读写锁该如何取舍
21、GO学习之 条件变量 sync.Cond
文章目录
- GO系列
- 前言
- sync.Cond 如何使用
前言
按照公司目前的任务,go 学习是必经之路了,虽然行业卷,不过技多不压身,依旧努力!!!
sync.Cond
是 Go 语言中实现的传统的条件变量。那什么是条件变量呢?一个条件变量可以理解为一个容器,容器中存放着一个或者一组等待着某个条件成立的 goroutine,当条件成立是这些处于等待状态的 goroutine 将得到通知并唤醒继续执行。就类似于比赛前跑到开始处预备好的运动员,等待裁判的一声枪响,砰的一声他们就开始狂奔了。
sync.Cond 如何使用
如果没有条件变量,我们可能在 goroutine 中通过连续轮询的方式检查是否满足条件然后继续执行。轮询是非常消耗资源的,因为 goroutine 在这个过程中处于活动状态但并没有实际工作进展,我们先来看一个使用 sync.Mutex
实现的条件轮询等待的例子,如下:
package mainimport ("fmt""sync""time"
)type signal struct{}// 控制条件
var ifOk boolfunc worker(i int) {fmt.Printf("Workder %d is working...\n", i)time.Sleep(1 * time.Second)fmt.Printf("Workder %d is finish...\n", i)
}func spawnGroup(f func(i int), num int, mu *sync.Mutex) <-chan signal {c := make(chan signal)// 声明一个线程组var wg sync.WaitGroup// 循环启动 5 个goroutinefor i := 0; i < num; i++ {wg.Add(1)go func(i int) {for {mu.Lock()if !ifOk {mu.Unlock()time.Sleep(100 * time.Millisecond)continue}mu.Unlock()fmt.Printf("worker %d: start to work... \n", i)f(i)wg.Done()return}}(i + 1)}// 启动一个 goroutine,等待着,直到 组里面 的 goroutine 数量为 0go func() {wg.Wait()c <- signal(struct{}{})}()return c
}func main() {fmt.Println("Start a group of workers...")// 初始化一个互斥锁mu := &sync.Mutex{}// 通过 spawnGroup 函数启动 5 个 goroutinec := spawnGroup(worker, 5, mu)time.Sleep(5 * time.Second)fmt.Println("The group of workers start to work...")mu.Lock()ifOk = truemu.Unlock()// 从通道中接受数据,这里只从通道中取出即可<-cfmt.Println("The group of workers work done!")
}
上面的示例是使用 sync.Mutex
来实现保护临界区资源的,不过性能上不够好,因为有很多空轮询是很消耗资源的。
sync.Cond
为 goroutine 在上述场景下提供了另一种可选的、资源消耗更小、使用体验更佳的同步方式,条件变量原语,避免轮询,用 sync.Cond
对上面的例子进行改造,如下:
package mainimport ("fmt""sync""time"
)type signal struct{}// 控制条件
var ifOk boolfunc worker(i int) {fmt.Printf("Workder %d is working...\n", i)time.Sleep(1 * time.Second)fmt.Printf("Workder %d is finish...\n", i)
}func spawnGroup(f func(i int), num int, cond *sync.Cond) <-chan signal {c := make(chan signal)// 声明一个线程组var wg sync.WaitGroup// 循环启动 5 个goroutinefor i := 0; i < num; i++ {wg.Add(1)go func(i int) {cond.L.Lock()for !ifOk {cond.Wait()}cond.L.Unlock()fmt.Printf("worker %d: start to work... \n", i)f(i)wg.Done()}(i + 1)}// 启动一个 goroutine,等待着,直到 组里面 的 goroutine 数量为 0go func() {wg.Wait()c <- signal(struct{}{})}()return c
}func main() {fmt.Println("Start a group of workers...")// 初始化一个条件锁cond := sync.NewCond(&sync.Mutex{})// 通过 spawnGroup 函数启动 5 个 goroutinec := spawnGroup(worker, 5, cond)time.Sleep(5 * time.Second)fmt.Println("The group of workers start to work...")cond.L.Lock()ifOk = true// 调用 sync.Cond 的 Broadcast方法后,阻塞的 goroutine 将被唤醒并从 wait 方法中返回cond.Broadcast()cond.L.Unlock()// 从通道中接受数据,这里只从通道中取出即可<-cfmt.Println("The group of workers work done!")
}
运行结果:
Start a group of workers...
The group of workers start to work...
worker 5: start to work...
Workder 5 is working...
worker 2: start to work...
Workder 2 is working...
worker 3: start to work...
Workder 3 is working...
worker 4: start to work...
Workder 4 is working...
worker 1: start to work...
Workder 1 is working...
Workder 5 is finish...
Workder 2 is finish...
Workder 3 is finish...
Workder 4 is finish...
Workder 1 is finish...
The group of workers work done!
上面的实例中,sync.Cond
实例的初始化需要一个满足实现了sync.Locker
接口的类型实例,通常使用 sync.Mutex
。条件变量需要互斥锁来同步临界区数据。各个等待条件成立的 goroutine 在加锁后判断条件是否成立,如果不成立,则调用 sync.Cond
的 Wait
方法进去等待状态。Wait 方法在 goroutine 挂起前会进行 Unlock
操作。
在 main 方法中将 ifOk 设置为 true 并调用了 sync.Cond
的 Broadcast
方法后,各个阻塞的 goroutine 将被唤醒并从 Wait 方法中返回。在 Wait 方法返回前,Wait 方法会再次加锁让 goroutine 进入临界区。接下来 goroutine 会再次对条件数据进行判定,如果人条件成立,则解锁并进入下一个工作阶段;如果条件还是不成立,那么再次调用 Wait 方法挂起等待。
现阶段还是对 Go 语言的学习阶段,想必有一些地方考虑的不全面,本文示例全部是亲自手敲代码并且执行通过。
如有问题,还请指教。
评论去告诉我哦!!!一起学习一起进步!!!
这篇关于GO学习之 条件变量 sync.Cond的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!