Go 中的数据竞争模型

Go 以其并发特性著称,但无限制的 goroutines 和共享内存可能导致数据竞争。本文分析了 Uber 在 Go 代码库中发现的多种数据竞争模型,包括通过引用捕获自由变量、切片操作、并发访问非线程安全的 map、值传递错误、混合使用通道和共享内存、WaitGroup 使用不当以及表驱动测试中的并行测试导致的问题。这些问题源于 Go 的语言设计和使用习惯,提示开发者应谨慎处理并发编程,避免数据竞争。
摘要由CSDN通过智能技术生成

of code (and growing) and contains approximately 2,100 unique Go services (and growing).

Uber 把Golang(简称 Go)作为开发微服务的主要编程语言。我们的 Go monorepo 仓库中大约包含了 5000 万行代码(这个数量还在增长),其中大约包含了 2100 个独立的 Go 服务(这个数量同样也在增长)。

并发作为 Go 中的一等公民;在函数调用之前使用 go 关键字就会以异步的方式调用。这些异步函数调用在 Go 中被称为 goroutines。开发者通过创建 goroutines 来缩短延迟时间(比如在 IO 或 RPC 调用等场景)。多个 goroutine 之间可以通过 (channels 来传递消息或者使用 共享内存 ) 来进行通信。共享内存恰好是 Go 中最常用的通信方式。

Go 程序员可以随意使用 goroutines, 因为它们被认为是 “轻量级” 并且创建 goroutines 是一件很容易的事。最后,我们注意到,在使用 Go 编写的程序通常比使用其他语言编写的程序表现出更高的并发性能。例如,通过扫描运行我们数据中心的数十万个微服务实例,我们发现使用 Go 编写的微服务表现出的并发能力大约是 java 的 8 倍。更高的并发也意味着可能发生更多并发错误。数据竞争是两个或者多个 goroutines 同时以无序并且至少有一个是以写的方式访问同一个数据时产生的并发错误。数据竞争是潜在的 bug,必须 不惜一切代价避免。

我们使用动态数据竞争检测技术开发了一个系统来检测 Uber 的数据竞争。这个系统在六个月的时间里,在我们的 Go 代码库中检测到大约 2,000 个数据竞争,其中我们的开发人员已经修复了大约 1,100 个数据竞争。

在这篇博客中,我们将展示我们在 Go 程序中发现的各种数据竞争模型。这项研究是通过分析 210 位独特的开发人员在六个月内修复的 1,100 多个数据竞争进行的。总的来说,我们注意到,由于某些语言设计选择,使得 Go 更容易引入数据竞争。其中语言特性和数据竞争之间存在复杂的相互作用。

Go 中的数据竞争模型_

我们研究了由开发者修复的约 1,100 个数据竞争的问题,并将它们划分为不同的类别。我们对这些数据竞争的研究显示了导致 Go 数据竞争的一些常见模型和一些神秘的原因:

1. 在 Go 的设计中,goroutines 可以以引用的方式不加限制地捕捉自由引用变量是引发数据竞争的重要因素

嵌套函数(又称闭包),在 Go 中可以通过引用的方式捕获所有自由变量。程序员不需要明确指定在闭包语法中捕获哪些自由变量。

这种使用模式与 Java 和 C ++ 不同。Java lambdas 只能够捕捉值,他们有意识地采取了该设计选择来避免并发错误 [1, 2]。C ++ 要求开发者明确指定按值还是引用的方式指定捕获方式。

尤其是在大闭包的情况下, 开发者通常不知道闭包内部使用的变量是通过引用方式捕获的自由变量。很多时候,Go 开发者通常会使用闭包函数作为 goroutines。由于使用引用捕捉和并发执行 goroutine, 除非有显式的同步操作,否则 Go 程序最终会以无序访问这些自由变量。我们用下面三个示例证明了这一点:

示例 1:在循环中捕捉临时变量引起的数据竞争

图 1A 中的代码显示了迭代 Go jobs 切片并通过 ProcessJob 函数处理每个 job 变量。

在这里,开发者将耗时的 ProcessJob 函数包装在匿名函数中,并且在每次循环遍历的时候都以 goroutine 的方式启动。但是,在循环中是在 goroutine 内部通过引用的方式来捕捉变量 job。当第一个循环迭代启动的 goroutine 访问了变量 job 时,父 goroutine 中的 for 循环将通过 slice 前进并更新变量 job 的值,以指向 slice 中的第二个元素,从而导致数据竞争。这种类型的数据竞争发生在循环体中对传值和引用类型元素进行读和写访问(包括切片,数组和 map)。Go 推荐一个编码习惯来隐藏和私有化循环体中的循环索引变量,很遗憾,开发者并不总是遵守这些规范。

示例 2:由于习惯捕获错误而引发的数据竞争。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值