golang 记一次data race排查过程

  • 背景
  • data race 现场
  • 解决思路
  • 经验总结

data race在写并发代码时候经常遇到,相关基础概念的介绍可以参考之前一篇文章:golang data race 竞态条件https://louyuting.blog.csdn.net/article/details/103606831

这篇文章主要是记录项目中一次 data race的排查过程。

背景

大概背景是这样的,最近项目中需要在golang中实现一个并发安全的list容器,list容器的长度不一定,通过在初始化的时候指定,容器必须要能够并发安全的更新容器内的每一个元素。经过调研,决定采用类似于 Java 中的 AtomicReferenceArray 的实现方案。关于AtomicReferenceArray 的实现原理这里就不细说了,网上很多博客介绍。

需求大概是这样,容器里面保存的是一个指针列表,每一个指针指向一个统计窗口的数据结构的地址。因为golang中的数组申明时候必须指定长度,不指定长度默认就是slice,所以也就是借助于slice来实现数组的目的。大概结构如下图:

里面的W0 - W(n-1) 都是指针,指针的size是8bytes(64位机器),指针分别指向一个实际的存储实体(用红色的圆来表示)。base指向数组的首地址,length表示数组的长度。

红色实体的数据结构大概是这样:

type bucket struct {startTime uint64value interface{}
}

需要实现的方法也比较简单,基本就是原子操作的常规套路, getcompareAndSet,最初版本源码大概是这样:

//该函数获取第 idx 个元素的地址
func (a *atomicPointerArray) offset(idx int) uintptr {......//省略
}func (a *atomicPointerArray) get(idx int) *bucket {// data race pointbucketPtr := *(**bucket)(unsafe.Pointer(a.offset(idx)))bucketPointer := unsafe.Pointer(bucketPtr)return (*bucket)atomic.LoadPointer(&bucketPointer)
}func (a *atomicPointerArray) compareAndSet(idx int, except, update *bucket) bool {offset := (**bucket)(unsafe.Pointer(a.offset(idx)))bucketPointer := (*unsafe.Pointer)unsafe.Pointer(offset)// data race pointreturn atomic.CompareAndSwapPointer(bucketPointer,unsafe.Pointer(except),unsafe.Pointer(update))
}

实际调用的代码就不给出了,大概就是存在多协程并发的调用 getcompareAndSet 方法。

data race 现场

如前面所描述,我用 go test -v -race ./... 来跑data race的时候就爆出了数据竞争,显示 get 函数的第一行和compareAndSet 函数的第三行存在data race。

这个问题很隐蔽…

get 函数的第一行:bucketPtr实际上第 idx 个元素,也就是一个 *bucket 指针,这里产生了一个对第idx个元素的读操作,但是这个读不是原子的,这里不仔细分析,很难分得清楚。
compareAndSet 函数的第三行:这里很简单,就是一个对第idx个元素写操作,写是原子的。

所以也就是多协程的一个并发读写问题。

解决思路

定位了问题解决起来就很简单了,把 get 函数的第一行的读取操作去掉也就OK了,顺便简化下代码:

func (a *atomicPointerArray) get(idx int) *bucket {return (*bucket)(atomic.LoadPointer((*unsafe.Pointer)(a.elementOffset(idx))))
}func (a *atomicPointerArray) compareAndSet(idx int, except, update *bucket) bool {return atomic.CompareAndSwapPointer((*unsafe.Pointer)(aa.elementOffset(idx)), unsafe.Pointer(except), unsafe.Pointer(update))
}

这样就get函数避免了一次对 *bucket 的读操作。

经验总结

从这个case里面还是有一些经验可以总结:

  1. 确定好哪些对象是并发访问的,对于并发访问的对象一定要做好读写分析;
  2. 不止是对象的读写分析,还有对象里面的属性如果存在并发读写访问也要做并发控制;
  3. 对于 指针 类型的变量,涉及到 unsafe.Pointer 时候一定要谨慎,任何非类型安全的指针转换成(*Obj)都是读操作,谨慎谨慎。
  4. 不要用 uintptr 做指针变量的访问,uintptr只用作地址数学运算,并且都放在一行内执行,避免野指针。

golang 记一次data race排查过程相关推荐

  1. 记一次网站故障排查过程(nginx 504状态码、 upstream timed out (110: Connection timed out)以及jbd2引起IO高

    一.问题描述 客户侧反馈无法正常访问系统,页面转圈,时好时坏,访问不稳定. 二.系统环境: 机器环境:UOS . nginx .php(对接其他服务器kingbase .钉钉.redis .KF) E ...

  2. 记一次OOM问题排查过程

    上周运维反馈线上程序出现了OOM,程序日志中的输出为 Exception in thread "http-nio-8080-exec-1027" java.lang.OutOfMe ...

  3. 记一次Kafka warning排查过程

    1.前因 在配合测试某个需求的时候,正好看到控制台打印了个报错,如下: 2023-03-06 17:05:58,565[325651ms][pool-28-thread-1][org.apache.k ...

  4. 记一次SOFA内存泄漏排查过程

    记一次内存泄漏排查过程 起因 某天中午大家还在安静的午休,睡得正香的时候突然被一阵手机滴-滴滴直响短信惊醒.一看是应用的服务器告警并且对应服务的所有机器都在告警"健康检查失败,自动拉下线&q ...

  5. golang的 data race 分析

    golang的data race 一.名词解析 1.data race: Any race is a bug 定义: ①多个线程(协程)对于同一个变量.②同时地.③进行读/写操作.并且④至少有一个线程 ...

  6. golang data race 竞态条件

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

  7. 多队列 部分队列没有包_记一次TCP全队列溢出问题排查过程

    简介:记一次TCP全队列溢出问题排查过程 1. 前言 本文排查的问题是经典的TCP队列溢出问题,因TCP队列问题在操作系统层面没有明显的指标异常,容易被忽略,故把排查过程分享给大家. 2. 问题描述 ...

  8. 记一次线上服务假死排查过程

    大家好,我是烤鸭: 最近线上问题有点多啊,分享一个服务假死的排查过程. 问题描述 9点10分,收到进程无响应报警(一共6台机器,有1台出现),后来又有1台出现. 排查思路 首先确认是否误报或者网络抖动 ...

  9. 记一次线上cpu飙升100%的排查过程

    大家好,我是烤鸭: 最近没怎么写技术文章,还是得回归下初心,正好前几天出现个线上问题,记录下排查过程. 问题描述 某个时间点,接收到接口响应慢报警. 过一会收到服务器cpu可用率低(<10%)报 ...

最新文章

  1. 开发日记-20190802 关键词 闲聊
  2. JAVA中类的访问修饰符的作用范围
  3. 安装gcc 4.8.2 for cxx 11
  4. Nginx之gzip压缩配置
  5. 如何解析一个字符串并返回一个嵌套数组?
  6. 有关Silverlight3.0在浏览器外运行的问题
  7. find结合rm删除或mv移动文件的方法
  8. 开课吧课堂之未被捕获的异常
  9. retorfit converter使用说明
  10. Lucene.Net
  11. fpga与海思BT1120调试问题记录
  12. 遗传算法bp神经网络原理,bp神经网络 遗传算法
  13. oracle拆分分区语法详解大全_Oracle分区表详解
  14. C#语言入门详解(刘铁锰)---泛型
  15. c语言井号花括号怎么打,大括号怎么打,手把手教你word大括号怎么输入
  16. uniapp扫描二维码问题
  17. 2012-8-18可樂美文分享《遗留在时…
  18. OS第七章 文件管理
  19. 小试debian-7.11.0-amd64+Plone5.1.2全文检索和预览中文WORD中文PDF
  20. GBK 和 UTF8编码

热门文章

  1. micro的介绍、安装与使用
  2. 提问之前我们应该做些什么 -Leo读提问的智慧 1
  3. git pull某单个文件
  4. 绿盟大赛-ModelArts实现智能花卉识别
  5. 安卓移动软件开发:简易录音软件实现
  6. 游戏开发的HelloWorld,快速入门,新手上路,使用CocosCreator+JS,flyBird(飞翔小鸟)
  7. 【小罗的hdlbits刷题笔记3】从Edgedetect对阻塞赋值和非阻塞赋值的思考
  8. 口红会染唇是什么意思_唇釉染唇是什么意思
  9. Shiro RememberMe 反序列化漏洞
  10. jQuery网页版简易qq音乐