分享
  1. 首页
  2. 文章

理解真实世界的并发Bug

oraoto · · 1243 次点击 · · 开始浏览
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

Go带来了新的并发原语和并发模式(其实也不太新),如果没有深入了解这些特性,一样会写出并发bug。

Understanding Real-World Concurrency Bugs in Go 这篇论文里,作者系统地分析了6个流行的Go项目(Docker、Kubernetes、gRPC-go、etcd、CockroachDB、 BoltD)和其中171个并发bug,通过这些分析我们可以加深对Go的并发模型的理解,从而产出更好、更可靠的代码。

Our study shows that it is as easy to make concurrency bugs with message passing as with shared memory,sometimes even more.
我们的研究表明,消息传递和共享内存一样、有时甚至更容易写出并发错误。

例如下面是k8s的一个bug,finishReq创建了一个子协程来执行fn然后通过select等待子协程完成或超时:

func finishReq(timeout time.Duration) r ob {
 ch :=make(chanob)
 // ch :=make(chanob, 1) // 修复方案
 go func() {
 result := fn()
 ch <- result // 阻塞
 }
 select {
 case
 result = <- ch
 return result
 case <- time.After(timeout)
 return nil
 }
 }
}

如果超时先发生,或者子协程和超时同时发生但go运行时选择了超时分支(非确定性),子协程就会永远阻塞。

Go并发模式使用情况

这一节分析了6个项目里goroutine、并发原语的使用情况。

goroutine使用情况

匿名函数的goroutine使用比普通函数要多,基本每1~5千行代码创建一个goroutine。

并发原语

虽然Go鼓励消息传递,但是在这些大项目里,共享内存的使用比消息传递要多,Mutex基本在channel的两倍以上。

Bug分类

这篇论文里,按两个维度对bug进行分类:

  1. 行为:阻塞和非阻塞,阻塞bug指goroutine意外地阻塞无法继续执行的情况(例如死锁),非阻塞bug通常是数据冲突
  2. 原因:共享内存和消息传递,因为用了这两种技术之一导致的bug

clipboard.png

可以看到,共享内存其实导致了更多的bug。

阻塞bug

clipboard.png

消息传递和共享内存导致的阻塞bug几乎一样多,而且消息传递的阻塞bug都和Go的消息传递语义例如channel有关,消息传递和共享内存一起使用的时候会很难发现bug。

例如Docker错误使用WaitGroup导致阻塞:

var group sync.WaitGroup
group.Add(len(pm.plugins))
for_, p := range pm.plugins {
 go func(p *plugin) {
 defer group.Done()
 }
 group.Wait() // 阻塞
}
// 应该在这里group.Wait()

错误使用channel和mutex导致阻塞:

func goroutine1() {
 m.Lock()
 ch <- request // 阻塞
 m.Unlock()
}
func goroutine2() {
 for{
 m.Lock() // 阻塞
 m.Unlock()
 request <- ch
 }
}

非阻塞bug

clipboard.png

共享内存导致更多的非阻塞bug,几乎是消息传递的8倍。

例如在下面这段代码里,每当ticker触发时执行一次f(),通过stopCh退出循环:

ticker := time.NewTicker()
for {
 f()
 select {
 case <- stopCh
 return
 case <- ticker
 }
}

但是select是非确定性的,stopChticker同时发生时,不一定会执行stopChan的分支,正确做法是先检查一次stopCh:

ticker := time.NewTicker()
for {
 select{
 case <- stopCh:
 return
 default:
 }
 f()
 select {
 case <- stopCh:
 return
 case <- ticker:
 }
}

参考


有疑问加站长微信联系(非本文作者)

本文来自:Segmentfault

感谢作者:oraoto

查看原文:理解真实世界的并发Bug

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

关注微信
1243 次点击
添加一条新回复 (您需要 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传

用户登录

没有账号?注册
(追記) (追記ここまで)

今日阅读排行

    加载中
(追記) (追記ここまで)

一周阅读排行

    加载中

关注我

  • 扫码关注领全套学习资料 关注微信公众号
  • 加入 QQ 群:
    • 192706294(已满)
    • 731990104(已满)
    • 798786647(已满)
    • 729884609(已满)
    • 977810755(已满)
    • 815126783(已满)
    • 812540095(已满)
    • 1006366459(已满)
    • 692541889

  • 关注微信公众号
  • 加入微信群:liuxiaoyan-s,备注入群
  • 也欢迎加入知识星球 Go粉丝们(免费)

给该专栏投稿 写篇新文章

每篇文章有总共有 5 次投稿机会

收入到我管理的专栏 新建专栏