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

看一个关于并发的例子

package main

import (
	"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.Mutex
	for n, i := 0, uint64(0); n < 5; n++ {
		go func() {
			for {
				m.Lock()
				i++
				v := i
				m.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 +0x65

Previous read at 0x00c000014148 by main goroutine:
  main.concurrencyDemo()
      /home/lfeng/GolandProjects/golang101/demo/concurrency.go:20 +0x87
  main.main()
      /home/lfeng/GolandProjects/golang101/demo/concurrency.go:11 +0xc4

Goroutine 7 (running) created at:
  main.concurrencyDemo()
      /home/lfeng/GolandProjects/golang101/demo/concurrency.go:21 +0x7a
  main.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 main

import (
	"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.Mutex
	i := uint64(0)
	for n := 0; n < 5; n++ {
		go func() {
			for {
				m.Lock()
				i++
				v := i
				m.Unlock()
				if isGold(v) {
					c <- v
				}
			}
		}()
	}
	for n := range c {
		fmt.Println("Found gold:", n)
	}
}

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

再看个例子

package main

import (
	"fmt"
	"runtime"
	"sync"
)

func main() {
	fmt.Println("golang version:", runtime.Version())
	concurrencyDemo()
}

func concurrencyDemo() {
	var wg sync.WaitGroup
	wg.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 +0xaf

Previous write at 0x00c000120028 by main goroutine:
  main.concurrencyDemo()
      /home/lfeng/GolandProjects/golang101/demo/concurrency2.go:17 +0xa4
  main.main()
      /home/lfeng/GolandProjects/golang101/demo/concurrency2.go:11 +0xc4

Goroutine 9 (running) created at:
  main.concurrencyDemo()
      /home/lfeng/GolandProjects/golang101/demo/concurrency2.go:18 +0x85
  main.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 main

import (
	"fmt"
	"runtime"
	"sync"
)

func main() {
	fmt.Println("golang version:", runtime.Version())
	concurrencyDemo()
}

func concurrencyDemo() {
	var wg sync.WaitGroup
	wg.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每次循环都会创建新变量

相关推荐

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

    2024-04-12 19:44:01       34 阅读
  2. loopvar 改动不同版本影响-基础循环

    2024-04-12 19:44:01       44 阅读
  3. loopvar 改动不同版本影响-defer,closures

    2024-04-12 19:44:01       37 阅读
  4. loopvar 改动不同版本影响-大循环执行时间

    2024-04-12 19:44:01       36 阅读
  5. STM32 不同时钟频率有什么不同影响

    2024-04-12 19:44:01       22 阅读
  6. HTTP不同版本区别

    2024-04-12 19:44:01       34 阅读
  7. Go语言中工作负载类型对并发影响

    2024-04-12 19:44:01       32 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-04-12 19:44:01       94 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-04-12 19:44:01       100 阅读
  3. 在Django里面运行非项目文件

    2024-04-12 19:44:01       82 阅读
  4. Python语言-面向对象

    2024-04-12 19:44:01       91 阅读

热门阅读

  1. 代码随想录算法训练营day37

    2024-04-12 19:44:01       31 阅读
  2. 【2024护网设备】椒图面试题

    2024-04-12 19:44:01       32 阅读
  3. c---内置函数模拟(memset,memcmp,memcpy,memmove)

    2024-04-12 19:44:01       31 阅读
  4. Go 源码之旅-开篇

    2024-04-12 19:44:01       38 阅读
  5. (一)ffmpeg 入门基础知识

    2024-04-12 19:44:01       36 阅读
  6. 在Ubuntu 20.04 LTS上安装MySQL 8.0的详细步骤讲解

    2024-04-12 19:44:01       37 阅读
  7. Array、Object、String、Number、Math常用方法

    2024-04-12 19:44:01       39 阅读
  8. 【ADB】adb、shell的介绍

    2024-04-12 19:44:01       38 阅读
  9. Python实现植物大战僵尸

    2024-04-12 19:44:01       28 阅读
  10. C# WinForm —— 05 控件简介

    2024-04-12 19:44:01       33 阅读
  11. 冒泡排序及qsort实现

    2024-04-12 19:44:01       39 阅读