【阅读总结】Understanding Real-World Concurrency Bugs in Go 小结

最近希望深入一下go 语言细节,在看过基本的语法后,开始找相关经典文章去阅读。虽然阅读总结大部分由GPT生成,但是我认为作为提示词和学习路径,还是有必要记录一下的。

个人总结在此:

以下文章由GPT生成,没有个人思考与总结。


《Understanding Real-World Concurrency Bugs in Go》由Tengfei Tu等人撰写。论文首次对Go语言实际程序中的并发错误进行了系统研究,分析171个并发错误,发现消息传递并不比共享内存更不易出错,某些情况下甚至更容易导致错误,研究结果有助于理解Go语言并发模型,为编写可靠的Go程序和开发调试诊断工具提供指导。

  1. 背景与研究目的:Go语言旨在简化并发编程,但其新的并发原语和机制对并发错误的影响尚不明确。本文通过对6个开源Go应用的研究,分析并发错误的成因、修复方法,比较消息传递和共享内存两种通信机制的易错性,为Go语言并发编程提供指导。
  2. Go语言并发机制与应用
  • 并发单元 - Goroutine:Go使用Goroutine作为并发单元,它是轻量级用户级线程,可通过在函数调用前加go关键字创建,支持匿名函数创建Goroutine,可能引发数据竞争。
  • 同步机制:支持共享内存同步,提供Mutex、RWMutex等原语;Once确保函数只执行一次;WaitGroup用于多个Goroutine完成共享变量访问的同步。还通过通道(chan)实现消息传递,有缓冲和无缓冲通道之分,select语句可用于等待多个通道操作。
  • 研究应用:选取Docker、Kubernetes等6个具有代表性的Go语言应用,这些应用在数据中心环境广泛使用,开发历史长且规模较大。
  1. Go语言并发使用模式
  • Goroutine使用:静态分析显示,6个应用中Goroutine创建频繁,平均每千行代码创建0.18 - 0.83个,多数应用使用匿名函数创建Goroutine更多。动态分析表明,与gRPC - C相比,gRPC - Go在运行时创建更多Goroutine,且Goroutine执行时间更短。
  • 并发原语使用:共享内存同步操作使用频率高于消息传递,Mutex是使用最广泛的原语,消息传递中chan使用频率最高。gRPC - Go使用的并发原语数量和种类均多于gRPC - C,且各应用并发原语使用情况随时间趋于稳定。
  1. 错误研究方法 - 错误收集:通过在GitHub提交历史中搜索并发相关关键词,筛选出3211个匹配提交,随机抽样并人工研究,最终确定171个并发错误。
  • 错误分类:根据错误行为分为阻塞错误(一个或多个Goroutine执行受阻)和非阻塞错误(Goroutine能完成任务但行为不符合预期);根据错误原因分为共享内存错误和消息传递错误。
  • 错误重现:回滚应用版本,构建并运行有错误的程序,依据错误报告中的症状判断是否成功重现错误。
  1. 阻塞错误
  • 根源:约42%由共享内存保护错误导致,如Mutex误用、RWMutex特有的优先级问题、WaitGroup和Cond使用不当;58%由消息传递错误引起,包括通道使用错误、通道与其他阻塞原语配合不当、消息传递库使用错误。
  • 修复:多数共享内存相关错误修复方法与传统语言类似,消息传递相关错误常通过添加消息或关闭操作、添加select语句、更换通道类型等方式修复。错误原因与修复策略、修复使用的原语有较高相关性,修复补丁平均6.8行代码。
  • 检测:Go内置死锁检测器只能检测出2个阻塞错误,因其设计目标为最小化运行时开销,未考虑部分运行中的Goroutine和等待其他系统资源的Goroutine,未来需开发新的检测技术。
  1. 非阻塞错误
  • 根源:约80%由共享内存保护失败引起,包括传统问题和Go新特性导致的问题,如匿名函数使用不当、WaitGroup误用、特殊库使用不当;约20%由消息传递错误引起,如通道误用、特殊库中通道使用不当。
  • 修复:约69%通过限制时间修复,如添加同步原语或移动现有原语;部分通过消除或绕过共享变量访问指令、复制共享变量等方式修复。Mutex和channel是常用的修复原语。
  • 检测:Go数据竞争检测器使用happen - before算法,能检测部分传统错误和匿名函数导致的错误,但因非阻塞错误类型多样、算法依赖Goroutine交错执行、存储历史信息有限等原因,无法检测所有非阻塞错误。
  1. 讨论与展望:消息传递不一定比共享内存更不易出错,它是阻塞错误的主要原因,且与其他机制结合时难以检测。研究揭示的错误代码模式可用于并发错误检测,未来应结合静态和动态技术开发更有效的检测工具。
  2. 结论:首次全面实证研究Go语言并发错误,发现消息传递和共享内存都可能导致并发错误,期望研究能加深对Go并发错误的理解,引起更多关注。
深入Go并发编程研讨课 Go提供了我们便利的进行并发编程的工具、方法和同步原语,同时也提供给我们诸多的犯错的机会,也就是俗称的“坑”。即使是顶级Go开发的项目,比如Docker、Kubernetes、gRPC、etcd, 都是有经验丰富的Go开发专家锁开发,也踩过不少的并发的坑,而且依然源源不断的继续踩着,即便是标准库也是这样。 分析和总结并发编程中的陷阱,避免重复踩在别人的坑中,正式本次培训课的重要内容。只有深入了解并发原语的实现,全面了解它们的特性和限制场景,注意它们的局限和容易踩的坑,才能提高我们的并发编程的能力。通过了解和学习其他人的经验和贡献的项目和库,我们可以更好的扩展我们的视野,避免重复的造轮子,或者说我们可以制作更好的轮子。 语言的内存模型定义了对变量的读写的可见性,可以清晰而准确读写事件的happen before关系。对于我们,可以很好地分析和编排goroutine的运行,避免数据的竞争和不一致的问题。 通过本次课程,你可以: 了解基本同步原语的具体实现、hack同步原语进行扩展,了解它们的使用场景和坑,已经别人是怎么踩的 了解一些扩展的同步源于,对于标准库sync包的补充 对于规模很大的项目,分布式同步原语是必不可少的,带你了解便利的分布式同步原语 atomic可以保证对数据操作的一致性,利用CAS可以设计lock-free的数据结构 channel是Go语言进行并发编程的很好的工具,带你了解它的使用姿势 了解Go语言的内存模型
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值