正确规避常见的 Go 并发陷阱

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

作为一门并发性能优越的语言,Go虽然降低了协程并发开发的难度,但也存在一些并发陷阱,这就需要我们在开发时额外注意。

我们再来介绍与Go 并发相关的几个小技巧,帮助你规避 Go 并发开发的一些陷阱。

闭包传递参数的问题

首先是循环并发时闭包传递参数的问题,如下错误例子所示:

func main()  {for i := 0 ; i < 5 ; i++{go func() {
fmt.Println("current i is ", i)
}()
}
time.Sleep(time.Second)
}复制代码

这段代码极有可能的输出为:

current i is 5
current i is 5
current i is 5
current i is 5
current i is 5复制代码

这是因为 i 使用的地址空间在循环中被复用,在 goroutine 执行时,i 的值可能在被主 goroutine 修改,而此时其他 goroutine 也在读取使用,从而导致了并发错误。针对这种错误可以通过复制拷贝或者传参拷贝的方式规避,如下所示:

func main()  {
	for i := 0 ; i < 5 ; i++{
		go func(v int) {
			fmt.Println("current i is", v)
		}(i)
	}
	time.Sleep(time.Second)
}复制代码

panic 异常

上一篇介绍 panic 时我们了解到 panic 异常的出现会导致 Go 程序的崩溃。但其实即使 panic 是出现在其他启动的子 goroutine 中,也会导致 Go 程序的崩溃退出,同时 panic 只能捕获 goroutine 自身的异常,因此对于每个启动的 goroutine,都需要在入口处捕获 panic,并尝试打印堆栈信息并进行异常处理,从而避免子 goroutine 的 panic 导致整个程序的崩溃退出。如下面的例子所示:

func RecoverPanic() {// 从 panic 中恢复并打印栈信息if e := recover(); e != nil {
buf := make([]byte, 1024)
buf = buf[:runtime.Stack(buf, false)]
fmt.Printf("[PANIC]%v\n%s\n", e, buf)
}
}func main() {for i:= 0 ; i < 5 ; i++{go func() {// defer 注册 panic 捕获函数defer RecoverPanic()
dothing()
}()
}
}复制代码

超时控制

最后一个技巧是要善于结合使用 select、timer 和 context 进行超时控制。在 goroutine 中进行一些耗时较长的操作,最好都加上超时timer,在并发的时候也要传递 context,这样在取消的时候就不会有遗漏,进而达到回收 goroutine 的目的,避免内存泄漏的发生。如下面的例子所示,通过 select 同时监听任务和定时器状态,在定时器到达而任务未完成之时,提前结束任务,清理资源并返回。

select {// do logic processcase msg <- input:
   ....// has been canceledcase <-ctx.Done():// ...资源清理return// 2 second timeout    case <-time.After(time.Second * 2)  // ...资源清理returndefault:
}复制代码

小结

本文我们主要介绍了Go 中常见的一些并发开发技巧。笔者也只是抛砖引玉,提出了并发开发技巧的三个技巧,其实不仅仅是 Go 并发,大家在日常开发中需要注意总结各个方面的开发技巧。

Serverless 架构就不要服务器了?

微服务架构中使用 ELK 进行日志采集以及统一处理

没有 try-catch,该如何处理 Go 错误异常?

订阅最新文章,欢迎关注我的公众号


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

本文来自:51CTO博客

感谢作者:mb6018e8479df66

查看原文:正确规避常见的 Go 并发陷阱

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

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