目录

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

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

Go 并发中有两个重要的概念:数据竞争 (data race) 和竞争条件 (race condition)

在并发程序中,竞争问题可能是程序面临的最难也是最不容易发现的错误之一

数据竞争 (data race)

当两个或多个协程同时访问同一个内存地址,并且至少有一个是在写时,就会发生数据竞争,看一下以下例子

i := 0
go func() {i++
}()go func() {i++
}()

当运行 go run -race main.go,会输出下面提示表明发生了数据竞争:

==================
WARNING: DATA RACE
Write at 0x00c00008e000 by goroutine 7:main.main.func2()
Previous write at 0x00c00008e000 by goroutine 6:main.main.func1()
==================

这段代码的 i 值是不可预知的,首先 i++ 是三个操作的组合:

  • i 中读取值 value
  • 将 value 的值 +1
  • 将值写回到 i

其次两个 goroutine 是同时进行的,导致会出现以下两个场景

场景一:Goroutine1 在 Goroutine2 之前运行完成

Goroutine1 Goroutine2 i 值
初始值 0
读取i的值value 0
将value值+1 0
将值写回到i 1
读取i的值value 1
将value值+1 1
将值写回到i 2

场景二:Goroutine1 和 Goroutine2 并发执行

Goroutine1 Goroutine2 i 值
初始值 0
读取i的值value 0
读取i的值value 0
将value值+1 0
将value值+1 0
将值写回到i 1
将值写回到i 1

这就是数据竞争造成的影响,如果两个协程同时访问同一块内存,并且至少有一个协程写入,就会导致一个不可预期的结果

避免数据竞争的发生

避免数据竞争可以使用以下三种方式:

  • 使用原子操作
  • 使用mutex对同一区域进行互斥操作
  • 使用管道 (channel) 进行通信以保证仅且只有一个协程在进行写操作

方案一:就是让 i++ 变成原子操作

众所周知,数据库的事务是一个 IT 人员必备的知识点。事务的四大特性:原子性 (Atomicity)、一致性 (Consistency)、隔离性 (Isolation)、持久性 (Durability)

原子操作意味着 “不可中断的一个或一系列操作”,保证多个线程对同一块内存的操作是串行的,保证数据的唯一性

在 Go 中,可以通过 atomic 包执行原子操作,示例:

var i int64
go func() {atomic.AddInt64(&i, 1)
}()
go func() {atomic.AddInt64(&i, 1)
}()

两个协程对 i 都是原子性的,一个原子操作不可中断,也就是另一个协程需要等待第一个协程的执行完成后才能对 i 操作;不过 atomic 包只能操作特定的类型 (如 int32,int64 等整数),而对于其他数据类型 (如切片,map 以及结构体) 就无法解决

方案二:同步原语 mutex

mutex 表示互斥,在 Go 中,sync 包提供了 Mutex 类型即互斥锁;互斥锁防止两个线程或协程同时对一个内存地址进行读写操作

数据竞争改为互斥锁的示例:

i := 0
mutex := sync.Mutex{}
go func() {mutex.Lock()i++mutex.Unlock()
}()go func() {mutex.Lock()i++mutex.Unlock()
}()

方案三:管道 (channel)

通过管道在多线程进行通信,避免同时读取同一块内存,示例:

i := 0
ch := make(chan int)
go func() {ch <- 1
}()go func() {ch <- 1
}()i += <-ch
i += <-ch

每个协程都将增量写入管道中,父协程管理管道并从管道中读取 i 进行写操作,只有一个协程对内存地址进行写操作,也就不存在数据竞争

竞争条件 (race condition)

看一下示例:

i := 0
mutex := sync.Mutex{}
go func() {mutex.Lock()defer mutex.Unlock()i = 1
}()go func() {mutex.Lock()defer mutex.Unlock()i = 2
}()

这里使用了 mutex 避免了数据竞争,但是输出的结果是不确定的。变量 i 的结果依赖于协程的执行顺序,可能是 1 也可能是 2。该示例不存在数据竞争,但存在 竞争条件 (race condition),也称为资源竞争。当程序的行为依赖于执行顺序或事件发生的时机不可控时就会发生竞争条件

保证协程间的执行顺序是协调和编排问题。如果要确保状态从0到1,然后再从1到2,我们就需要找到一种保证协程按序执行的方式。一种方式就是使用管道来解决该问题。此外,如果我们使用了管道进行协调和编排,也可以保证在同一时间只有一个协程在访问公共的部分,这也就意味着可以移除mutex

总结

数据竞争 (data race) 的发生条件是:当多个协程同时访问一个相同内存地址,并且至少有一个在进行写操作时,数据竞争意味着不确定的行为

而不存在数据竞争不代表结果就是确定的。实际上,一个应用程序即使不存在数据竞争,但它的行为肯依赖于不可控的发生时间或执行顺序,这就是竞争条件 (race condition)

参考

[1] Go错误集锦 | 通过示例理解数据竞争及竞争条件

Go 并发 | 数据竞争及竞争条件相关推荐

  1. 多线程并发中什么是竞争条件?

    跟着作者的65节课彻底搞懂Java并发原理专栏,一步步彻底搞懂Java并发原理. 作者简介:笔名seaboat,擅长工程算法.人工智能算法.自然语言处理.计算机视觉.架构.分布式.高并发.大数据和搜索 ...

  2. 机器学习竞争其实是一场数据上的竞争

    来源:网络大数据 摘要:人工智能的三大发展要素已经是老生常谈了.算法.算力和数据对机器学习的重要性和声望不亚于"谦哥"的喝酒.烫头和抽烟. 那些热衷竞争实施机器学习的公司现在惊讶地 ...

  3. 2022-2027年中国数据银行市场竞争态势及行业投资前景预测报告

    [报告类型]产业研究 [报告格式]电子+纸介版 [出品单位]华经产业研究院 本报告由华经产业研究院重磅推出,对中国数据银行行业的发展现状.竞争格局及市场供需形势进行了具体分析,并从行业的政策环境.经济 ...

  4. 第6章 基于锁的并发数据结构设计

    第6章 基于锁的并发数据结构设计 本章主要内容 ※并发数据结构设计的意义 指导如何设计 ※实现为并发设计的数据结构 在上一章中,我们对底层原子操作和内存模型有了详尽的了解.在本章中,我们将先将底层 的 ...

  5. R语言dplyr包的mutate函数将列添加到dataframe中或者修改现有的数据列:基于条件判断创建布尔型指示变量、将异常离散编码转化为NA值

    R语言dplyr包的mutate函数将列添加到dataframe中或者修改现有的数据列:基于条件判断创建布尔型指示变量.将异常离散编码转化为NA值 目录

  6. MySQL删除退出后数据未更新,mysql一不小心删除了数据或更新了数据没有加where 条件...

    mysql一不小心删除了数据或更新了数据没有加where 条件 1,show variables like '%log_bin%'; 2.show master logs; 3.show master ...

  7. Spring Boot实战解决高并发数据入库: Redis 缓存+MySQL 批量入库

    前言 最近在做阅读类的业务,需要记录用户的PV,UV: 项目状况:前期尝试业务阶段: 特点: 快速实现(不需要做太重,满足初期推广运营即可) 快速投入市场去运营 收集用户的原始数据,三要素: 谁 在什 ...

  8. R语言对dataframe行数据进行筛选(row selection)、筛选数据行、基于条件筛选数据行

    R语言对dataframe行数据进行筛选(row selection).筛选数据行.基于条件筛选数据行 目录

  9. TCP高并发数据转接服务器(Ntrip Caster)

    TCP高并发数据转接服务器(Ntrip Caster) 说到NTRIP Caster, 咱们需要首先链接一下什么是Ntrip协议由于这不是本博客的重点,故只做如下简单介绍,,大家可以通过如下链接对nt ...

最新文章

  1. PhiSpy:在细菌基因组中识别噬菌体
  2. matlab中gen2par函数,R语言中绘图par()函数用法
  3. 广西2021各校高考成绩查询入口,2021年广西高考成绩排名查询系统,广西高考位次排名查询...
  4. Linux centosVMware zip压缩工具、tar打包、打包并压缩
  5. java 阻塞 wait_java交替打印奇偶数问题,会出现2个线程都wait阻塞了
  6. 旅游社交网站 游范儿
  7. 力扣1281.整数的各位积和之差
  8. C++ string是否包含某字符串方法(已封装)
  9. python函数赋值给对象_【Python核心编程笔记】一、Python中一切皆对象
  10. IOS解惑(1)之@property(nonatomic,getter=isOn) BOOL on;中的getter解惑
  11. 什么是机器学习?(下)
  12. 此博客不再更新,新博客地址https://xsamsara.tk/
  13. 2008是中国的奥运年
  14. 完美数及寻找完美数的算法(Perfect number‘s algorithm)
  15. 线性代数知识点总结之行列式
  16. 适合新手入门—嵌入式C语言
  17. 计算机专业面试银行的自我介绍,银行面试自我介绍1分钟
  18. Simulink三相异步电机仿真(1)
  19. C语言视频教程-谭浩强版-小甲鱼主讲—P13
  20. 服务器部署服务采坑记录(不断更新)

热门文章

  1. Memos:V-REP/Coppeliasim视觉传感器的使用
  2. Key exchange was not finished,connection is closed近期遇到这个错误sshd更新导致的
  3. 医院HIS系统运维工作见闻
  4. 2022年系统集成项目管理工程师考试,需要知道这些
  5. openid无效什么意思_“洋河年份原浆”商标被驳回,为什么古井贡酒坐不住了?...
  6. SDUT--OJ《数据结构与算法》实践能力专题训练6 图论
  7. faststone capture使用长截图(滚动截图)功能教程
  8. wordpress 调用指定页面内容详解2 get_children()
  9. 八、CPython语法改动实验:增加“非”与“前缀自增”
  10. ksps什么单位_IEEE754扩充精度算法异步转换速率的550KSPS