【Go】数据竞争探究

一、引入

观察如下程序

var val = 0

func main() {
	go add()
	go add()
	time.Sleep(1 * time.Second)
	fmt.Println(val)
}

func add() {
	for i := 0; i < 100; i++ {
		val++
	}
}

在Java中,如果两个线程同一个变量加100次,那么结果是未知的,但是绝大数情况下val的值是小于200的。但是运行结果却是200(我试了好几次都是这个结果)
在这里插入图片描述

二、解释

在这里插入图片描述
这是修改后的程序以及运行结果,似乎找到了原因。因为add()运行太快了,第一个协程运行完成后第二个协程才开始运行。加上Sleep后,两个协程能交替执行,才会出现数据竞争。

三、再深一步

  1. 为什么数据竞争这么“严重”
    理论上,5ms对于cpu来说是很长的,两个协程同时修改val概率没有这么大,所以结果在100~200直接,会更靠近200些。
  2. 上面是公司的笔记本运行结果,而在我的台式机上结果还是200,这是为什么

第一个问题,涉及到内存同步,参考链接:曹政谈重排。两个协程的操作大部分只写到各自的store buffer中,最终内存同步时,才会有大量自增是无效的。
第二个问题,我开始认为我的电脑是单核的,但是看配置却是
在这里插入图片描述
然后,我怀疑是我的Go环境没有配置正确(使用的默认值)

GOMAXPROCS:在Go 1.5版本之前,GOMAXPROCS参数的值默认是1

我没有设置GOMAXPROCS,而Go的版本是1.16,GOMAXPROCS没有设置的话,有几个核就会使用几个核,并且加上如下代码,结果显示并不是单核运行

	num := runtime.NumCPU()
	fmt.Println("使用cpu数量", num) // 使用cpu数量 12

然后,我又怀疑两个协程没有交替运行,于是我又改进了程序,将val改变的中间值,以及哪个协程修改打印了出来

var val = 0

func main() {
	num := runtime.NumCPU()
	fmt.Println("使用cpu数量", num)
	go add("A")
	go add("B")
	time.Sleep(1 * time.Second)
	fmt.Println("val的最终结果", val)
}

func add(proc string) {
	for i := 0; i < 100; i++ {
		val++
		fmt.Printf("execute process[%s] and val is %d\n", proc, val)
		time.Sleep(5 * time.Millisecond)
	}
}

但是结果如下
在这里插入图片描述

在这里插入图片描述
证明两个协程是交替运行的,我又迷惑了

四、结尾

感谢rustyx的解惑data-race question
上面程序有两个需要改进的地方

  1. 主协程sleep时间太短了,笔记本上运行结果118可能是因为add()协程没有运行完。
  2. val自增的次数(100)太少了,一个协程先跑完,另一个还没开始运行

于是,我又改进了代码

var val = 0

func main() {
	num := runtime.NumCPU()
	fmt.Println("使用cpu数量", num)
	var wg = sync.WaitGroup{}
	wg.Add(2)
	go add("A", &wg)
	go add("B", &wg)
	wg.Wait()
	fmt.Println("val的最终结果", val)
}
func add(proc string, wg *sync.WaitGroup) {
	defer wg.Done()
	for i := 0; i < 1000; i++ {
		val++
	}
}

这是笔记本上运行的一次结果
在这里插入图片描述
下面是台式机上运行的结果
在这里插入图片描述
终于,可以看到发生数据竞争了。当然,更多的运行结果还是2000

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值