GO学习之 条件变量 sync.Cond

2023-12-02 23:20
文章标签 go 条件 学习 变量 cond sync

本文主要是介绍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.CondWait 方法进去等待状态。Wait 方法在 goroutine 挂起前会进行 Unlock 操作。
在 main 方法中将 ifOk 设置为 true 并调用了 sync.CondBroadcast 方法后,各个阻塞的 goroutine 将被唤醒并从 Wait 方法中返回。在 Wait 方法返回前,Wait 方法会再次加锁让 goroutine 进入临界区。接下来 goroutine 会再次对条件数据进行判定,如果人条件成立,则解锁并进入下一个工作阶段;如果条件还是不成立,那么再次调用 Wait 方法挂起等待。


现阶段还是对 Go 语言的学习阶段,想必有一些地方考虑的不全面,本文示例全部是亲自手敲代码并且执行通过。
如有问题,还请指教。
评论去告诉我哦!!!一起学习一起进步!!!

这篇关于GO学习之 条件变量 sync.Cond的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/447223

相关文章

Java 接口定义变量的示例代码

《Java接口定义变量的示例代码》文章介绍了Java接口中的变量和方法,接口中的变量必须是publicstaticfinal的,用于定义常量,而方法默认是publicabstract的,必须由实现类... 在 Java 中,接口是一种抽象类型,用于定义类必须实现的方法。接口可以包含常量和方法,但不能包含实例

MySQL 筛选条件放 ON后 vs 放 WHERE 后的区别解析

《MySQL筛选条件放ON后vs放WHERE后的区别解析》文章解释了在MySQL中,将筛选条件放在ON和WHERE中的区别,文章通过几个场景说明了ON和WHERE的区别,并总结了ON用于关... 今天我们来讲讲数据库筛选条件放 ON 后和放 WHERE 后的区别。ON 决定如何 "连接" 表,WHERE

MybatisPlus中几种条件构造器运用方式

《MybatisPlus中几种条件构造器运用方式》QueryWrapper是Mybatis-Plus提供的一个用于构建SQL查询条件的工具类,提供了各种方法如eq、ne、gt、ge、lt、le、lik... 目录版本介绍QueryWrapperLambdaQueryWrapperUpdateWrapperL

Go异常处理、泛型和文件操作实例代码

《Go异常处理、泛型和文件操作实例代码》Go语言的异常处理机制与传统的面向对象语言(如Java、C#)所使用的try-catch结构有所不同,它采用了自己独特的设计理念和方法,:本文主要介绍Go异... 目录一:异常处理常见的异常处理向上抛中断程序恢复程序二:泛型泛型函数泛型结构体泛型切片泛型 map三:文

C++,C#,Rust,Go,Java,Python,JavaScript的性能对比全面讲解

《C++,C#,Rust,Go,Java,Python,JavaScript的性能对比全面讲解》:本文主要介绍C++,C#,Rust,Go,Java,Python,JavaScript性能对比全面... 目录编程语言性能对比、核心优势与最佳使用场景性能对比表格C++C#RustGoJavapythonjav

Go语言实现桥接模式

《Go语言实现桥接模式》桥接模式是一种结构型设计模式,它将抽象部分与实现部分分离,使它们可以独立地变化,本文就来介绍一下了Go语言实现桥接模式,感兴趣的可以了解一下... 目录简介核心概念为什么使用桥接模式?应用场景案例分析步骤一:定义实现接口步骤二:创建具体实现类步骤三:定义抽象类步骤四:创建扩展抽象类步

GO语言实现串口简单通讯

《GO语言实现串口简单通讯》本文分享了使用Go语言进行串口通讯的实践过程,详细介绍了串口配置、数据发送与接收的代码实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要... 目录背景串口通讯代码代码块分解解析完整代码运行结果背景最近再学习 go 语言,在某宝用5块钱买了个

Nginx内置变量应用场景分析

《Nginx内置变量应用场景分析》Nginx内置变量速查表,涵盖请求URI、客户端信息、服务器信息、文件路径、响应与性能等类别,这篇文章给大家介绍Nginx内置变量应用场景分析,感兴趣的朋友跟随小编一... 目录1. Nginx 内置变量速查表2. 核心变量详解与应用场景3. 实际应用举例4. 注意事项Ng

Go 使用环境变量的实现小结

《Go使用环境变量的实现小结》作为软件开发人员,在项目中管理配置变量的重要性,本文主要介绍在Golang中处理环境变量的强大工具github.com/joho/godotenv包,利用这个包,你可以... 目录步js骤 1:安装步骤 2:制作 .env 文件步骤android 3:加载环境变量步骤 4:利用

GO语言zap日志库理解和使用方法示例

《GO语言zap日志库理解和使用方法示例》Zap是一个高性能、结构化日志库,专为Go语言设计,它由Uber开源,并且在Go社区中非常受欢迎,:本文主要介绍GO语言zap日志库理解和使用方法的相关资... 目录1. zap日志库介绍2.安装zap库3.配置日志记录器3.1 Logger3.2 Sugared