golang race condition 竞态条件

  • data race
  • race condition
  • golang race detector

golang的协程机制使得编写并发代码变得非常容易,但是多线程(协程)编程离不开的一个话题就是线程(协程)安全,也就是并发环境下是不是存在竞态条件。

这里有两个概念首先需要区分清楚:竞态条件(race condition) 和 数据竞争 (data race)。

data race

定义:①多个线程(协程)对于同一个变量、②同时地、③进行读/写操作、并且④至少有一个线程进行写操作。(也就是说,如果所有线程都是只进行读操作,那么将不构成数据争用)

后果:如果发生了数据争用,读取该变量时得到的值将变得不可知(根据内存模型),使得该多线程程序的运行结果将完全不可预测,有一定可能会导致直接崩溃。

如何防止:对于有可能被多个线程同时访问的变量使用排他访问控制,具体方法包括使用mutex(互斥量)或者使用atomic变量。

race condition

相对于数据争用(data race),竞态条件(race condition)指的是更加高层次的更加复杂的现象,一般需要在设计并行程序时进行细致入微的分析,才能确定。(也就是隐藏得更深)

定义:受各线程上代码执行的顺序和时机的影响,程序的运行结果产生(预料之外)的变化。

后果:如果存在竞态条件(race condition),多次运行程序对于同一个输入将会有不同的结果,但结果并非完全不可预测,它将由输入数据和各线程的执行顺序共同决定。

如何预防:竞态条件产生的原因很多是对于同一个资源的一系列连续操作并不是原子性的,也就是说有可能在执行的中途被其他线程抢占,同时这个“其他线程”刚好也要访问这个资源。解决方法通常是:将这一系列操作作为一个critical section(临界区)。

golang race detector

golang 提供了race检测工具,这个竞态检测工具是在编译流程中内置检测代码到应用程序。一旦你的程序开始运行,它能够发现和报告任何他所检测到的竞态情况。 这是非常棒的功能, 为识别缺陷代码做出了非常重要的工作。

下面从实例出发来看data race:

package mainimport ("fmt""sync""testing"
)func Test_normal(t *testing.T)  {Wait := &sync.WaitGroup{}var Counter = 0Wait.Add(2)for routine := 1; routine <= 2; routine++ {go Routine(Wait, &Counter)}Wait.Wait()fmt.Printf("Final Counter: %d\n", Counter)
}func Routine(Wait *sync.WaitGroup, Counter *int) {for count := 0; count < 2; count++ {value := *Countervalue++*Counter = value}Wait.Done()
}

这个程序很简单,并发的执行:

value := *Counter //读
value++
*Counter = value//写

这里很明显有并发问题,变量 *Counter 存在并发读写的情况。

我们跑一下 go test -race 结果如下:

==================
WARNING: DATA RACE
Read at 0x00c0000a4080 by goroutine 10:command-line-arguments.Routine()/Users/ytlou/Desktop/golang/golang_study/study/basic/race/data_race1_test.go:23 +0x47Previous write at 0x00c0000a4080 by goroutine 9:command-line-arguments.Routine()/Users/ytlou/Desktop/golang/golang_study/study/basic/race/data_race1_test.go:25 +0x5dGoroutine 10 (running) created at:command-line-arguments.Test_normal()/Users/ytlou/Desktop/golang/golang_study/study/basic/race/data_race1_test.go:15 +0xeftesting.tRunner()/usr/local/go/src/testing/testing.go:909 +0x199Goroutine 9 (finished) created at:command-line-arguments.Test_normal()/Users/ytlou/Desktop/golang/golang_study/study/basic/race/data_race1_test.go:15 +0xeftesting.tRunner()/usr/local/go/src/testing/testing.go:909 +0x199
==================
Final Counter: 4
--- FAIL: Test_normal (0.00s)testing.go:853: race detected during execution of test
FAIL
FAIL    command-line-arguments  0.848s
FAIL

提示很友好的告诉了存在并发读写的情况。但是我们实际运行这个代码,结果却在大部分情况下都是正确的,输出4,这也是并发问题难以排查的地方。

这里假设输出是2,我们看一下协程执行流程:(主内存和协程内存之间的关系背景假设已知)

上面没有画全,但是已经能够说明问题,一句话说明就是:没有保证有并发读写情况下的排他性。

即使将上面核心的读写改成一行:

*Counter = *Counter + 1

但是这一行在编译器编译之后也是好几个机器指令,所以并发问题并不会解决。

所以解决的办法就是:

Lock.Lock()
*Counter = *Counter + 1
Lock.Unlock()

保证排他。没有发现任何竞态条件。

这时候 go test race 就能够pass。

golang data race 竞态条件相关推荐

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

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

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

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

  3. eclipse运行go test_在 Go 中发现竞态条件 (Race Conditions)

    当我意识到我一直在处理和解决的问题有一个专有名词描述的时候,我总会觉得这事十分有趣.这次出现这种情况的是竞争条件(Race Conditions).当你处理多个 routine 共享某类资源的时候,不 ...

  4. python 很高兴问题_Python 3.7曾有一个很老的GIL竞态条件(race condition),我是这么解决的...

    Python部落(python.freelycode.com)组织翻译,禁止转载,欢迎转发. 作者:Victor Stinner 作为Python最关键的组成部分之一:GIL(全局解释器锁),我花了4 ...

  5. 竞态条件(race condition)

    在学习多线程的过程中,因为是非科班学生,操作系统的东西都是一知半解的,所以很多名词都没有理解,另外具体的Java虚拟机如何工作还需要后续的学习,这里只能慢慢学习了,等到这本书看完好好读读操作系统的东西 ...

  6. 雪城大学信息安全讲义 五、竞态条件

    五.竞态条件 原文:Race Condition Vulnerability 译者:飞龙 1 竞态条件漏洞 下面的代码段属于某个特权程序(即 Set-UID 程序),它使用 Root 权限运行. 1: ...

  7. 竞态条件的赋值_信号-sunshine225-51CTO博客

    一.基础知识信号产生的条件 a. 终端按键产生.如:ctrl+c(SIGINT信号),ctrl+\(SIGQUIT信号),ctrl+z(SIGTSTP信号)...... b. 系统命令和函数.如:ki ...

  8. 计算机系统学习之(1):基础知识概要——进程、中断、线程、竞态条件、关键区域、死锁、进程调度

    文章目录 进程的创建 哪些事件导致进程的创建 fork 和 exec 命令创建和控制进程 fork() 命令 execve() 命令 进程的状态 中断 中断的种类 线程 线程共享内容 线程独有内容 进 ...

  9. Unix/Linux编程:竞态条件与sigsuspend函数

    利用pause和alarm函数实现sleep #include <unistd.h>int pause(void); pause函数使调用进程挂起直到有信号到达.如果信号的处理动作是终止进 ...

最新文章

  1. ubuntu安装使用ffmpeg
  2. scala集合同时支持不可变集合和可变集合
  3. C++数据类型和变量类型。
  4. mui用ajax上拉加载更多,mui上拉加载更多的使用
  5. ubuntu 两块硬盘挂载不上_win10 轉 Ubuntu
  6. mysql配置文件结构_MariaDB/MySQL配置文件my.cnf解读
  7. mybatis内部类映射写法
  8. java home_JAVA_HOME配置
  9. Hello软件项目相关功能测试点
  10. 简单74LS138介绍(抄自原理图)
  11. vscode 格式化文件配置说明
  12. css重复,CSS小课堂之可重复渐变
  13. 2022年危险化学品生产单位安全生产管理人员操作证考试题库及答案
  14. 工资倒挂,IT 行业薪资潜规则如何规避?
  15. 计算机二级题目之字符串练习学习
  16. 机器学习中精确率(precision)、召回率(recall)和准确率(accuracy)的理解
  17. matlab由图像得到复振幅程序,基于Matlab的空间滤波实验的计算机仿真
  18. 小孩孩子应用题计算机错误,为什么一二年级的孩子数学应用题总出错?
  19. 无线技巧:学会设置无线上网猫以及网卡
  20. vue之vue-cli

热门文章

  1. 双系统苹果Mac笔记本如何切换windows系统?
  2. 如何选择有效的CRM软件解决方案
  3. python自动化工具之pywinauto(三)
  4. Bzoj1502【NOI2005】月下柠檬树
  5. 电子零售商进军百货业务 棘手的供应链(转)
  6. Vue手脚架的配置以及使用
  7. python打包成exe fail to ex_“failed to excute script xxx” PyInstaller 打包python程序为exe文件过程错误...
  8. 产品需求文档写作:工友APP(PRD)
  9. vue-cli3使用iview
  10. php 26个字母输出三角形,php使用for语句输出三角形的方法