当前位置: 首页 > news >正文

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每次循环都会创建新变量

相关文章:

  • 4.2.k8s的pod-标签管理、镜像拉取策略、容器重启策略、资源限制、优雅终止
  • Clion 输出乱码 解决方案
  • LeetCode热题100
  • 编程:不只是工作,是我生活的一部分
  • Linux服务篇之FTP及SFTP
  • 数字电子技术基础入门(三)
  • [xboard]real6410-3 S3C6410光盘资料与功能测试
  • Pandas学习笔记——第二弹
  • C++修炼之路之string--标准库中的string
  • neo4j图数据库下载安装配置
  • 【C++造神计划】printf 与 cout
  • Mysql底层原理四:B+树索引
  • Python对docx文本一些操作
  • C++ 11 新特性:内存对齐 alignof 和 alignas
  • 蓝桥杯-求阶乘
  • Android路由框架AnnoRouter:使用Java接口来定义路由跳转
  • create-react-app做的留言板
  • django开发-定时任务的使用
  • Docker 1.12实践:Docker Service、Stack与分布式应用捆绑包
  • Electron入门介绍
  • java2019面试题北京
  • orm2 中文文档 3.1 模型属性
  • Shell编程
  • Zsh 开发指南(第十四篇 文件读写)
  • 程序员最讨厌的9句话,你可有补充?
  • 后端_ThinkPHP5
  • 记一次和乔布斯合作最难忘的经历
  • 如何抓住下一波零售风口?看RPA玩转零售自动化
  • 三栏布局总结
  • 使用SAX解析XML
  • 云大使推广中的常见热门问题
  • linux 淘宝开源监控工具tsar
  • Redis4.x新特性 -- 萌萌的MEMORY DOCTOR
  • #if和#ifdef区别
  • (cljs/run-at (JSVM. :browser) 搭建刚好可用的开发环境!)
  • (delphi11最新学习资料) Object Pascal 学习笔记---第7章第3节(封装和窗体)
  • (Note)C++中的继承方式
  • (Oracle)SQL优化技巧(一):分页查询
  • (附源码)springboot优课在线教学系统 毕业设计 081251
  • (删)Java线程同步实现一:synchronzied和wait()/notify()
  • (十)T检验-第一部分
  • (转)IOS中获取各种文件的目录路径的方法
  • (转)winform之ListView
  • (转载)CentOS查看系统信息|CentOS查看命令
  • ./和../以及/和~之间的区别
  • .net core 微服务_.NET Core 3.0中用 Code-First 方式创建 gRPC 服务与客户端
  • .Net Core与存储过程(一)
  • @CacheInvalidate(name = “xxx“, key = “#results.![a+b]“,multi = true)是什么意思
  • [04]Web前端进阶—JS伪数组
  • [1204 寻找子串位置] 解题报告
  • [1525]字符统计2 (哈希)SDUT
  • [17]JAVAEE-HTTP协议
  • [Android]通过PhoneLookup读取所有电话号码
  • [Angular] 笔记 8:list/detail 页面以及@Input
  • [BZOJ 4034][HAOI2015]T2 [树链剖分]