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

相关文章

Java并发编程必备之Synchronized关键字深入解析

《Java并发编程必备之Synchronized关键字深入解析》本文我们深入探索了Java中的Synchronized关键字,包括其互斥性和可重入性的特性,文章详细介绍了Synchronized的三种... 目录一、前言二、Synchronized关键字2.1 Synchronized的特性1. 互斥2.

Linux卸载自带jdk并安装新jdk版本的图文教程

《Linux卸载自带jdk并安装新jdk版本的图文教程》在Linux系统中,有时需要卸载预装的OpenJDK并安装特定版本的JDK,例如JDK1.8,所以本文给大家详细介绍了Linux卸载自带jdk并... 目录Ⅰ、卸载自带jdkⅡ、安装新版jdkⅠ、卸载自带jdk1、输入命令查看旧jdkrpm -qa

Tomcat版本与Java版本的关系及说明

《Tomcat版本与Java版本的关系及说明》:本文主要介绍Tomcat版本与Java版本的关系及说明,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录Tomcat版本与Java版本的关系Tomcat历史版本对应的Java版本Tomcat支持哪些版本的pythonJ

MySQL中慢SQL优化的不同方式介绍

《MySQL中慢SQL优化的不同方式介绍》慢SQL的优化,主要从两个方面考虑,SQL语句本身的优化,以及数据库设计的优化,下面小编就来给大家介绍一下有哪些方式可以优化慢SQL吧... 目录避免不必要的列分页优化索引优化JOIN 的优化排序优化UNION 优化慢 SQL 的优化,主要从两个方面考虑,SQL 语

IDEA中Git版本回退的两种实现方案

《IDEA中Git版本回退的两种实现方案》作为开发者,代码版本回退是日常高频操作,IntelliJIDEA集成了强大的Git工具链,但面对reset和revert两种核心回退方案,许多开发者仍存在选择... 目录一、版本回退前置知识二、Reset方案:整体改写历史1、IDEA图形化操作(推荐)1.1、查看提

Python异步编程中asyncio.gather的并发控制详解

《Python异步编程中asyncio.gather的并发控制详解》在Python异步编程生态中,asyncio.gather是并发任务调度的核心工具,本文将通过实际场景和代码示例,展示如何结合信号量... 目录一、asyncio.gather的原始行为解析二、信号量控制法:给并发装上"节流阀"三、进阶控制

Redis中高并发读写性能的深度解析与优化

《Redis中高并发读写性能的深度解析与优化》Redis作为一款高性能的内存数据库,广泛应用于缓存、消息队列、实时统计等场景,本文将深入探讨Redis的读写并发能力,感兴趣的小伙伴可以了解下... 目录引言一、Redis 并发能力概述1.1 Redis 的读写性能1.2 影响 Redis 并发能力的因素二、

JDK多版本共存并自由切换的操作指南(本文为JDK8和JDK17)

《JDK多版本共存并自由切换的操作指南(本文为JDK8和JDK17)》本文介绍了如何在Windows系统上配置多版本JDK(以JDK8和JDK17为例),并通过图文结合的方式给大家讲解了详细步骤,具有... 目录第一步 下载安装JDK第二步 配置环境变量第三步 切换JDK版本并验证可能遇到的问题前提:公司常

nvm如何切换与管理node版本

《nvm如何切换与管理node版本》:本文主要介绍nvm如何切换与管理node版本问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录nvm切换与管理node版本nvm安装nvm常用命令总结nvm切换与管理node版本nvm适用于多项目同时开发,然后项目适配no

Mybatis从3.4.0版本到3.5.7版本的迭代方法实现

《Mybatis从3.4.0版本到3.5.7版本的迭代方法实现》本文主要介绍了Mybatis从3.4.0版本到3.5.7版本的迭代方法实现,包括主要的功能增强、不兼容的更改和修复的错误,具有一定的参考... 目录一、3.4.01、主要的功能增强2、selectCursor example3、不兼容的更改二、