loopvar 改动不同版本的影响-并发

2024-04-10 21:52

本文主要是介绍loopvar 改动不同版本的影响-并发,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

看一个关于并发的例子

package mainimport ("fmt""runtime""sync"
)func main() {fmt.Println("golang version:", runtime.Version())concurrencyDemo()
}func concurrencyDemo() {isGold := func(num uint64) bool {return num%65536 == 0}var c = make(chan uint64)var m sync.Mutexfor n, i := 0, uint64(0); n < 5; n++ {go func() {for {m.Lock()i++v := im.Unlock()if isGold(v) {c <- v}}}()}for n := range c {fmt.Println("Found gold:", n)}
}

这个代码定义了一个isGold 的func,可以被65536整除的就是gold,然后定义了一个uint64的channel,接着开了5个go routine,每个go routine都是一个不会停止的协程,加锁i进行自增,并把自增后的i复制给v,如果v可以被65536整除,那么把v放到uint64的channel中,最后打印出channel的值。
如果代码写的不好,并发的时候是很容易出现data race的,运行的时候加上-race这个参数

golang 1.21 运行结果

 go run -race demo/concurrency.go
golang version: go1.21.5
Found gold: 65536
Found gold: 131072
Found gold: 196608
Found gold: 262144
Found gold: 327680
Found gold: 393216
Found gold: 458752
Found gold: 524288
Found gold: 589824
Found gold: 655360
^Csignal: interrupt

golang 1.22运行结果

go run -race demo/concurrency.go
golang version: go1.22.1
==================
WARNING: DATA RACE
Write at 0x00c000014148 by goroutine 7:main.concurrencyDemo.func2()/home/lfeng/GolandProjects/golang101/demo/concurrency.go:24 +0x65Previous read at 0x00c000014148 by main goroutine:main.concurrencyDemo()/home/lfeng/GolandProjects/golang101/demo/concurrency.go:20 +0x87main.main()/home/lfeng/GolandProjects/golang101/demo/concurrency.go:11 +0xc4Goroutine 7 (running) created at:main.concurrencyDemo()/home/lfeng/GolandProjects/golang101/demo/concurrency.go:21 +0x7amain.main()/home/lfeng/GolandProjects/golang101/demo/concurrency.go:11 +0xc4
==================
Found gold: 65536
Found gold: 65536
Found gold: 65536
Found gold: 65536
Found gold: 131072
Found gold: 131072
Found gold: 131072
Found gold: 65536
^Csignal: interrupt

可以看到1.21是没有data race的,但是1.22存在了data race

解决方法

把i的定义移到循环外面,修改后的代码

package mainimport ("fmt""runtime""sync"
)func main() {fmt.Println("golang version:", runtime.Version())concurrencyDemo()
}func concurrencyDemo() {isGold := func(num uint64) bool {return num%65536 == 0}var c = make(chan uint64)var m sync.Mutexi := uint64(0)for n := 0; n < 5; n++ {go func() {for {m.Lock()i++v := im.Unlock()if isGold(v) {c <- v}}}()}for n := range c {fmt.Println("Found gold:", n)}
}

修改后的代码不会存在data race了

再看个例子

package mainimport ("fmt""runtime""sync"
)func main() {fmt.Println("golang version:", runtime.Version())concurrencyDemo()
}func concurrencyDemo() {var wg sync.WaitGroupwg.Add(5)for i := 0; i < 5; i++ {go func() {defer wg.Done()fmt.Println(i)}()}
}

这个例子其实很简单,使用waitGroup来控制协程的数量和协程的结束。协程中只是打印循环变量i

golang 1.21的运行结果

go run -race demo/concurrency2.go
golang version: go1.21.5
5
5
==================
WARNING: DATA RACE
Read at 0x00c000120028 by goroutine 9:main.concurrencyDemo.func1()/home/lfeng/GolandProjects/golang101/demo/concurrency2.go:20 +0xafPrevious write at 0x00c000120028 by main goroutine:main.concurrencyDemo()/home/lfeng/GolandProjects/golang101/demo/concurrency2.go:17 +0xa4main.main()/home/lfeng/GolandProjects/golang101/demo/concurrency2.go:11 +0xc4Goroutine 9 (running) created at:main.concurrencyDemo()/home/lfeng/GolandProjects/golang101/demo/concurrency2.go:18 +0x85main.main()/home/lfeng/GolandProjects/golang101/demo/concurrency2.go:11 +0xc4
==================
5
5
5
Found 1 data race(s)
exit status 66

golang 1.22运行结果

go run -race demo/concurrency2.go
golang version: go1.22.1
2
1
4
3
0

发现1.21存在data race,但是1.22是不存在data race的

解决方法

go func增加参数传递循环的i
修改后的代码

package mainimport ("fmt""runtime""sync"
)func main() {fmt.Println("golang version:", runtime.Version())concurrencyDemo()
}func concurrencyDemo() {var wg sync.WaitGroupwg.Add(5)for i := 0; i < 5; i++ {go func(i int) {defer wg.Done()fmt.Println(i)}(i)}
}

修改后的代码也不会存在data race

结论

上面两个例子出现data race的原因都是1.21 loop 变量是初始化一次,但是1.22每次循环都会创建新变量

这篇关于loopvar 改动不同版本的影响-并发的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

在macOS上安装jenv管理JDK版本的详细步骤

《在macOS上安装jenv管理JDK版本的详细步骤》jEnv是一个命令行工具,正如它的官网所宣称的那样,它是来让你忘记怎么配置JAVA_HOME环境变量的神队友,:本文主要介绍在macOS上安装... 目录前言安装 jenv添加 JDK 版本到 jenv切换 JDK 版本总结前言China编程在开发 Java

go动态限制并发数量的实现示例

《go动态限制并发数量的实现示例》本文主要介绍了Go并发控制方法,通过带缓冲通道和第三方库实现并发数量限制,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面... 目录带有缓冲大小的通道使用第三方库其他控制并发的方法因为go从语言层面支持并发,所以面试百分百会问到

Go语言并发之通知退出机制的实现

《Go语言并发之通知退出机制的实现》本文主要介绍了Go语言并发之通知退出机制的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧... 目录1、通知退出机制1.1 进程/main函数退出1.2 通过channel退出1.3 通过cont

java如何实现高并发场景下三级缓存的数据一致性

《java如何实现高并发场景下三级缓存的数据一致性》这篇文章主要为大家详细介绍了java如何实现高并发场景下三级缓存的数据一致性,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 下面代码是一个使用Java和Redisson实现的三级缓存服务,主要功能包括:1.缓存结构:本地缓存:使

使用jenv工具管理多个JDK版本的方法步骤

《使用jenv工具管理多个JDK版本的方法步骤》jenv是一个开源的Java环境管理工具,旨在帮助开发者在同一台机器上轻松管理和切换多个Java版本,:本文主要介绍使用jenv工具管理多个JD... 目录一、jenv到底是干啥的?二、jenv的核心功能(一)管理多个Java版本(二)支持插件扩展(三)环境隔

MySQL版本问题导致项目无法启动问题的解决方案

《MySQL版本问题导致项目无法启动问题的解决方案》本文记录了一次因MySQL版本不一致导致项目启动失败的经历,详细解析了连接错误的原因,并提供了两种解决方案:调整连接字符串禁用SSL或统一MySQL... 目录本地项目启动报错报错原因:解决方案第一个:第二种:容器启动mysql的坑两种修改时区的方法:本地

conda安装GPU版pytorch默认却是cpu版本

《conda安装GPU版pytorch默认却是cpu版本》本文主要介绍了遇到Conda安装PyTorchGPU版本却默认安装CPU的问题,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的... 目录一、问题描述二、网上解决方案罗列【此节为反面方案罗列!!!】三、发现的根本原因[独家]3.1 p

Redis指南及6.2.x版本安装过程

《Redis指南及6.2.x版本安装过程》Redis是完全开源免费的,遵守BSD协议,是一个高性能(NOSQL)的key-value数据库,Redis是一个开源的使用ANSIC语言编写、支持网络、... 目录概述Redis特点Redis应用场景缓存缓存分布式会话分布式锁社交网络最新列表Redis各版本介绍旧

IIS 7.0 及更高版本中的 FTP 状态代码

《IIS7.0及更高版本中的FTP状态代码》本文介绍IIS7.0中的FTP状态代码,方便大家在使用iis中发现ftp的问题... 简介尝试使用 FTP 访问运行 Internet Information Services (IIS) 7.0 或更高版本的服务器上的内容时,IIS 将返回指示响应状态的数字代

python多线程并发测试过程

《python多线程并发测试过程》:本文主要介绍python多线程并发测试过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、并发与并行?二、同步与异步的概念?三、线程与进程的区别?需求1:多线程执行不同任务需求2:多线程执行相同任务总结一、并发与并行?1、