前言

两个或多个 goroutine 访问同一个资源(如变量或数据结构),并尝试对该资源进行读写而不考虑其他 goroutine。这种类型的代码可以创建你见过的最疯狂和最随机的 bug。通常需要大量的日志记录和运气才能找到这些类型的bug。


提示:以下是本篇文章正文内容,下面案例可供参考

一、data race 会出现什么结果呢?

例子 一

package mainimport ("fmt""sync"
)var Counter int = 0
var wg sync.WaitGroupfunc main() {for i := 0; i < 100000; i++ {for routine := 1; routine <= 2; routine++ {wg.Add(1)go Routine(routine)}wg.Wait()}fmt.Println(Counter)
}
//399718 399574 399634
func Routine(id int) {for conut := 0; conut < 2; conut++ {Counter++}//399618 399650wg.Done()
}

399718 399574 399634 这是上面代码 执行3次的结果 每次都不一样

我们来分析一下:

最好的情况 4 Routine1 读 和 写  执行完 Routine2 才开始执行 读取到的是结果加2的数据 在次累加

最坏的情况:2  Routine1 读 和 写 中间 Routine2 开始执行了 读取到未累加的数据

这里还有一个问题 i++ 是不是原子的?

在上面这个例子执行完就知道不是原子的了。我们来看看 低层汇编的代码

0x0031 00049 (main.go:29) MOVQ    "".Counter(SB), AX  //将  Counter 变量复制给 AX
0x0038 00056 (main.go:29) INCQ    AX                  //将  AX ++
0x003b 00059 (main.go:29) MOVQ    AX, "".Counter(SB)  //将  AX 重新赋值给  Counter

二 如何检测  data race

Go 1.1中,Go 工具引入了一个 race detector。

竞争检测器是在构建过程中内置到程序中的代码。

然后,一旦你的程序运行,它就能够检测并报告它发现的任何竞争条件。

它非常酷,并且在识别罪魁祸首的代码方面做了令人难以置信的工作。

例子 一 全局变量

package mainimport ("fmt""sync"
)var Counter int = 0
var wg sync.WaitGroupfunc main() {for i := 0; i < 100000; i++ {for routine := 1; routine <= 2; routine++ {wg.Add(1)go Routine(routine)}wg.Wait()}fmt.Println(Counter)
}
//399718 399574 399634
func Routine(id int) {for conut := 0; conut < 2; conut++ {Counter++fmt.Println(Counter)}/**0x0031 00049 (main.go:29)       MOVQ    "".Counter(SB), AX0x0038 00056 (main.go:29)       INCQ    AX0x003b 00059 (main.go:29)       MOVQ    AX, "".Counter(SB)*///399618 399650wg.Done()
}

这个例子跟 上面那个例子一样,我们试一下 go run -race main.go

结果如下

routine8  在 0x00000063eec8  地址读的时候 routine7 在 0x00000063eec8  这个地址上写入

例子 二 临时变量

func main() {var wg sync.WaitGroupwg.Add(5)for i := 0; i < 5; i++ {go func() {fmt.Println(i)wg.Done()}()}wg.Wait()
}

这个代码在 很多面试题种也会提到这个 请问 打印的结果是什么??

结果的答案 是不确定的。但是常见的但是 是 会出现 5个 5

我们来检测一下 结果出现 两个争抢

三 single machine word 相关的例子

大家看一下 ,下面的代码

single machine word 在64位是 8字节的  在go中 一个指针 类型的 也是8 字节

这个例子会出现的  data race 吗

package mainimport "fmt"type IceCreamMaker interface {// Hello greets a customerHello()
}type Ben struct {name string
}func (b *Ben) Hello() {fmt.Printf("Ben says, \"Hello my name is %s\"\n", b.name)
}type Jerry struct {name string
}func (j *Jerry) Hello() {fmt.Printf("Jerry says, \"Hello my name is %s\"\n", j.name)
}func main() {var ben = &Ben{"Ben"}var jerry = &Jerry{"Jerry"}var maker IceCreamMaker = benvar loop0, loop1 func()loop0 = func() {maker = bengo loop1()}loop1 = func() {maker = jerrygo loop0()}go loop0()for {maker.Hello()}
}

我们先执行看一下  多试一下  发现结果中好像混入一个神奇的数据

Ben says, "Hello my name is Ben"
Jerry says, "Hello my name is Jerry"
Jerry says, "Hello my name is Jerry"
Ben says, "Hello my name is Jerry"
Ben says, "Hello my name is Ben"

Ben says, "Hello my name is Jerry" 为什么会出现 这个类 Ben 他居然不知道他是谁 发神经了吗?指针赋值不是 原子的吗????

interface 类型

接口在概念上是一个具有两个字段的结构。

类型指向一个结构,该结构描述实现此接口的值的类型。

数据指向实现本身的值。Data 的内容作为任何通过接口调用的方法的接收者传递。

type interface struct {Type uintptr     // points to the type of the interface implementationData uintptr     // holds the data for the interface's receiver
}

对于 var maker IceCreamMaker = ben 语句,编译器将生成执行以下操作的代码。

接口的 Type 字段设置为指向 * Ben 类型的定义,

Data 字段包含 Ben 的副本,即指向 Ben 值的指针。

当 loop1()执行语句 maker = jerry 时,必须更新接口值的两个字段。

Type 现在指向 * Jerry 的定义,而 Data 包含一个指向 Jerry 实例的指针。

Go 内存模型说,写入单个 single machine word 将是原子的,但接口是两个字的值。

当接口值被更改时,另一个 goroutine 可能会观察到它的内容。

在这种情况下,它可能会看到这样的东西,

所以 Jerry 的 Hello ()函数被调用了。

这个案例的两个结构体的内存布局一模一样所以出现错误也不会 panic 退出。

如果在里面再加入一个 string 的字段,去读取就会导致 panic。

这种错误在线上实在太难发现了,如果 有人把结构体的类型更改了,就会造成非常严重的错误


文献

Ice cream makers and data races – The acme of foolishness

Ice Cream Makers and Data Races Part II

https://medium.com/a-journey-with-go/go-discovery-of-the-trace-package-e5a821743c3c

Introducing the Go Race Detector - go.dev

Share Memory By Communicating - go.dev  这个是官方共享内存的文档

总结

没有安全的 data race(safe data race)。您的程序要么没有 data race,要么其操作未定义。

原子性

可见行

go 并发编程 之 数据竞争 data race (三)相关推荐

  1. 并发编程的数据竞争问题以及解决之道

    Go语言以容易进行并发编程而闻名,但是如果稍不注意,并发程序可能导致的数据竞争问题(data race)就会经常出现在你编写的并发程序的待解决Bug列表中-- 如果你不幸在代码中遇到这种错误,这将是最 ...

  2. 数据争用(data race) 和竞态条件(race condition)

    在有关多线程编程的话题中,数据争用(data race) 和竞态条件(race condition)是两个经常被提及的名词,它们两个有着相似的名字,也是我们在并行编程中极力避免出现的.但在处理实际问题 ...

  3. 别混淆数据争用(data race) 和竞态条件(race condition)

    在有关多线程编程的话题中,数据争用(data race) 和竞态条件(race condition)是两个经常被提及的名词,它们两个有着相似的名字,也是我们在并行编程中极力避免出现的.但在处理实际问题 ...

  4. 高并发编程-Thread_正确关闭线程的三种方式

    文章目录 概述 stop() Deprecated 方式一 设置开关 方式二 调用interrupt API 方式三 暴力结束线程-> Daemon Thread + interrupt API ...

  5. Go 并发 | 数据竞争及竞争条件

    目录 Go 并发 | 数据竞争及竞争条件 数据竞争 (data race) 避免数据竞争的发生 竞争条件 (race condition) 总结 参考 Go 并发 | 数据竞争及竞争条件 Go 并发中 ...

  6. golang data race 竞态条件

    golang race condition 竞态条件 data race race condition golang race detector golang的协程机制使得编写并发代码变得非常容易,但 ...

  7. Go并发编程里的数据竞争以及解决之道

    Go语言以容易进行并发编程而闻名,但是如果稍不注意,并发程序可能导致的数据竞争问题(data race)就会经常出现在你编写的并发程序的待解决Bug列表中-- 如果你不幸在代码中遇到这种错误,这将是最 ...

  8. actor 模型 锁 java_漫谈并发编程:Actor模型

    0x00 前言 一般来说有两种策略用来在并发线程中进行通信:共享数据和消息传递.熟悉c和java并发编程的都会比较熟悉共享数据的策略,比如java程序员就会常用到 java.util.concurre ...

  9. 【读书笔记】Java并发编程的艺术

    第一章 并发编程的挑战 上下文切换 上下文切换概述 切出:一个线程被剥夺处理器的使用权而暂定运行 切入:一个线程被选中占用处理器或者继续运行 上下文:在这种切入切出的过程中,操作系统需要保存和恢复相应 ...

最新文章

  1. jQuery学习笔记(一)
  2. 关于tcp、http可能你想知道的那些事
  3. vs.net 2005 中自定义模版项
  4. “睡服”面试官系列第十三篇之函数的扩展(建议收藏学习)
  5. js赋值与逻辑运算的疑问
  6. hypermesh 连接单元_船舶方向测量单元motion sensor
  7. 被神话的Linux, 一文带你看清Linux在多核可扩展性设计上的不足
  8. AKS解决方案架构设计
  9. Vue学习三:安装axios(实现http请求后端)
  10. java集成极光推送
  11. 软考中级真题 2015年上半年 系统集成项目管理工程师 应用技术
  12. yigo基础学习笔记5_yigo二次开发配置
  13. Pandas之十二速查手册
  14. 通信工程考研英语复试专有名词翻译
  15. hive hive beeline常用参数
  16. android悬浮窗口的实现
  17. 【技术】5G技术的应用场景及发展趋势
  18. 微机原理与接口技术 | 一、微型计算机系统
  19. 青云服务器控制台系统,青云QingCloud控制台小程序上线
  20. 计算机课说话检查200字,上课说话检讨书200字

热门文章

  1. 阿里云AI训练营第一天
  2. micro的介绍、安装与使用
  3. 看看开发者服务在过去的这8个月谁又圈钱了
  4. 建造者模式-JAVA代码实现
  5. You can't take the sky from me
  6. 肖申克的救赎(转贴)
  7. 51nod - 1204 Parity
  8. GitLab WorkFlow
  9. 《Python编程 从入门到实践》 一、基础知识 第六章 字典
  10. 大学计算机基础教育改革,谈计算机等级考试引导大学计算机基础教育改革.pdf...