golang 确保并发安全性

embedded/2024/9/23 6:29:35/

golang_0">golang并发安全性

在Golang中,并发安全性通常指的是当多个goroutines同时访问同一个数据结构或资源时,能够保证数据的一致性和完整性,避免数据竞争、死锁等问题

并发安全性案例

案例1

创建 count,起1000个goroutines,做一亿次自增运算,代码如下:

func main() {wg := sync.WaitGroup{}var count = 0for i := 0; i < 1000; i++ {wg.Add(1)go func() {defer wg.Done()for i := 0; i < 10000; i++ {count++}}()}wg.Wait()fmt.Println(count)
}

预计结果:

100000000

执行结果:

test go run main.go 
656188

原因:

count++非原子性操作,在串行场景下,不会出现一致性问题,但是在并发场景下,多个goroutines并发操作同一个变量,就会出现goroutinescount值相互覆盖的情况,导致一致性问题

案例2

通过rpc接口,并行多次数据请求,并把返回结果做整合

func main() {wg := sync.WaitGroup{}var list []intfor i := 0; i < 1000; i++ {wg.Add(1)go func() {defer wg.Done()// rpc请求,结果并进行整合list = append(list, rpcCall()...)}()}wg.Wait()fmt.Println(len(list))
}// rpc 调用
func rpcCall() []int {var ret []intfor i := 0; i < 100; i++ {ret = append(ret, rand.Intn(10000))}return ret
}

预计结果:

100000

执行结果:

test go run main.go 
21500

append 方法不是原子操作,并发情况下存在一致性问题

解决办法

  • 使用互斥锁(Mutex):通过使用互斥锁来保护共享资源的访问,一次只允许一个goroutine访问共享资源,从而避免竞争条件
  • 使用原子操作(Atomic Operations):对于简单的读写操作,可以使用原子操作来保证操作的原子性,避免竞争条件
  • 使用通道(Channel):通过使用通道来进行goroutine之间的通信和同步,避免共享资源的直接访问
  • 使用并发安全的数据结构,例如 sync.Map等
案例1 优化方案

通过共享锁解决问题:

func main() {// 添加互斥锁mu := sync.Mutex{}wg := sync.WaitGroup{}var count = 0for i := 0; i < 1000; i++ {wg.Add(1)go func() {defer wg.Done()for i := 0; i < 10000; i++ {// 锁住共享资源mu.Lock()count++mu.Unlock()}}()}wg.Wait()fmt.Println(count)
}

执行结果:

test go run main.go 
10000000

通过原子操作解决问题:

func main() {wg := sync.WaitGroup{}var count int32 = 0for i := 0; i < 1000; i++ {wg.Add(1)go func() {defer wg.Done()for i := 0; i < 10000; i++ {atomic.AddInt32(&count, 1)}}()}wg.Wait()fmt.Println(count)
}

执行结果:

test go run main.go 
10000000
案例2 优化方案

通过channel + select-case解决问题

func main() {// 创建channelch := make(chan []int, 100)var list []intfor i := 0; i < 1000; i++ {go func(ch chan []int) {// rpc结果写入channel中ch <- rpcCall()}(ch)}// 轮询等待每个goroutine的执行结果for i := 0; i < 1000; i++ {select {case v := <-ch:list = append(list, v...)}}fmt.Println(len(list))
}// rpc 调用
func rpcCall() []int {var ret []intfor i := 0; i < 100; i++ {ret = append(ret, rand.Intn(10000))}return ret
}

执行结果:

test go run main.go 
100000

http://www.ppmy.cn/embedded/32093.html

相关文章

华为机考入门python3--(19)牛客19- 简单错误记录

分类&#xff1a;字符串 知识点&#xff1a; 分割字符串 my_str.split(\\) 字符串只保留最后16位字符 my_str[-16:] 列表可以作为队列、栈 添加元素到第一个位置 my_list.insert(0, elem) 增加元素到最后一个位置 my_list.append(elem) 删除第一个 my_list.pop(0)…

C语言实验-学生信息管理系统

按以下菜单界面编写学生信息管理系统&#xff1b; 1&#xff09;录入学生信息首先输入学生人数&#xff0c;然后根据学生人数开辟动态数组&#xff1b; 2&#xff09;学生信息包括学号、姓名、性别、三门课成绩、总分&#xff1b;其中学号、姓名、 性别、三门课成绩是需要从键盘…

element_Plus中表格和分页的使用

HTML 表格&#xff08;:data"filterData"绑定的数据&#xff09; <el-table ref"multipleTableRef" :data"filterData" style"width: 100%"selection-change"handleSelectionChange"><el-table-column type"…

使用docker创建rocketMQ主从结构,使用

1、 创建目录 mkdir -p /docker/rocketmq/logs/nameserver-a mkdir -p /docker/rocketmq/logs/nameserver-b mkdir -p /docker/rocketmq/logs/broker-a mkdir -p /docker/rocketmq/logs/broker-b mkdir -p /docker/rocketmq/store/broker-a mkdir -p /docker/rocketmq/store/b…

JAVAEE—servlet的概念及使用,使用servlet接口实现一个表白墙

文章目录 servlet的概念静态页面和动态页面servlet的作用 写出一个servlet程序目录的创建设置smart tomcat编写helloworld servlet的概念 首先我们要搞明白什么是servlet&#xff0c;servlet是一种实现动态页面的技术&#xff0c;他是由tomcat提供给程序员的一组API可以帮助程…

STM32 F103C8T6学习笔记17:类IIC通信(SMBus协议)—MLX90614红外非接触温度计

今日学习配置MLX90614红外非接触温度计 与 STM32 F103C8T6 单片机的通信 文章提供测试代码讲解、完整工程下载、测试效果图 本文需要用到的大概基础知识&#xff1a;1.3寸OLED配置通信显示、IIC通信、 定时器配置使用 这里就只贴出我的 OLED驱动方面的网址链接了&#xff1a…

使用Sentio产品对Sui生态进行深入地数据分析和调试

Sentio最近在Sui上推出了Dash和Debugger这两个重要产品&#xff0c;为Sui生态系统中的开发者和用户提供了关键的工具&#xff0c;以增强其体验。这些产品是Sentio作为基础设施提供商的重要一步&#xff0c;使其与专门为Sui生态系统量身定制的索引、数据分析和监控能力并驾齐驱。…

CKEditor编辑器的简单使用方法,取值,赋值

先从官网下载包。CKEditor 4 - Download Latest Version. 一&#xff1a;在项目里引用JQ基础包和CK的JS包 <script src"/JS/jquery-3.4.1.js?v1.0"></script><script src"/ckeditor/ckeditor.js"></script> 二&#xff1a;在表…