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

相关文章

SpringIntegration消息路由之Router的条件路由与过滤功能

《SpringIntegration消息路由之Router的条件路由与过滤功能》本文详细介绍了Router的基础概念、条件路由实现、基于消息头的路由、动态路由与路由表、消息过滤与选择性路由以及错误处理... 目录引言一、Router基础概念二、条件路由实现三、基于消息头的路由四、动态路由与路由表五、消息过滤

go中空接口的具体使用

《go中空接口的具体使用》空接口是一种特殊的接口类型,它不包含任何方法,本文主要介绍了go中空接口的具体使用,具有一定的参考价值,感兴趣的可以了解一下... 目录接口-空接口1. 什么是空接口?2. 如何使用空接口?第一,第二,第三,3. 空接口几个要注意的坑坑1:坑2:坑3:接口-空接口1. 什么是空接

利用Go语言开发文件操作工具轻松处理所有文件

《利用Go语言开发文件操作工具轻松处理所有文件》在后端开发中,文件操作是一个非常常见但又容易出错的场景,本文小编要向大家介绍一个强大的Go语言文件操作工具库,它能帮你轻松处理各种文件操作场景... 目录为什么需要这个工具?核心功能详解1. 文件/目录存javascript在性检查2. 批量创建目录3. 文件

Go语言中最便捷的http请求包resty的使用详解

《Go语言中最便捷的http请求包resty的使用详解》go语言虽然自身就有net/http包,但是说实话用起来没那么好用,resty包是go语言中一个非常受欢迎的http请求处理包,下面我们一起来学... 目录安装一、一个简单的get二、带查询参数三、设置请求头、body四、设置表单数据五、处理响应六、超

Java进阶学习之如何开启远程调式

《Java进阶学习之如何开启远程调式》Java开发中的远程调试是一项至关重要的技能,特别是在处理生产环境的问题或者协作开发时,:本文主要介绍Java进阶学习之如何开启远程调式的相关资料,需要的朋友... 目录概述Java远程调试的开启与底层原理开启Java远程调试底层原理JVM参数总结&nbsMbKKXJx

Nginx中location实现多条件匹配的方法详解

《Nginx中location实现多条件匹配的方法详解》在Nginx中,location指令用于匹配请求的URI,虽然location本身是基于单一匹配规则的,但可以通过多种方式实现多个条件的匹配逻辑... 目录1. 概述2. 实现多条件匹配的方式2.1 使用多个 location 块2.2 使用正则表达式

Golang基于内存的键值存储缓存库go-cache

《Golang基于内存的键值存储缓存库go-cache》go-cache是一个内存中的key:valuestore/cache库,适用于单机应用程序,本文主要介绍了Golang基于内存的键值存储缓存库... 目录文档安装方法示例1示例2使用注意点优点缺点go-cache 和 Redis 缓存对比1)功能特性

Go 1.23中Timer无buffer的实现方式详解

《Go1.23中Timer无buffer的实现方式详解》在Go1.23中,Timer的实现通常是通过time包提供的time.Timer类型来实现的,本文主要介绍了Go1.23中Timer无buff... 目录Timer 的基本实现无缓冲区的实现自定义无缓冲 Timer 实现更复杂的 Timer 实现总结在

Go使用pprof进行CPU,内存和阻塞情况分析

《Go使用pprof进行CPU,内存和阻塞情况分析》Go语言提供了强大的pprof工具,用于分析CPU、内存、Goroutine阻塞等性能问题,帮助开发者优化程序,提高运行效率,下面我们就来深入了解下... 目录1. pprof 介绍2. 快速上手:启用 pprof3. CPU Profiling:分析 C

使用Go语言开发一个命令行文件管理工具

《使用Go语言开发一个命令行文件管理工具》这篇文章主要为大家详细介绍了如何使用Go语言开发一款命令行文件管理工具,支持批量重命名,删除,创建,移动文件,需要的小伙伴可以了解下... 目录一、工具功能一览二、核心代码解析1. 主程序结构2. 批量重命名3. 批量删除4. 创建文件/目录5. 批量移动三、如何安