前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >谈谈go语言编程的并发安全

谈谈go语言编程的并发安全

作者头像
李海彬
发布2018-03-26 14:09:30
1.4K0
发布2018-03-26 14:09:30
举报
文章被收录于专栏:Golang语言社区

问题起因

在分布式存储开源项目 Weed-FS 中, 我发现了一个地方非并发安全(not concurrency-safety), 所以提交了一个 Weed-FS-PullRequest-75 来进行加锁保护。 简化这个问题如下:

当有一个变量, 有一个 goroutine 会对它进行写操作, 其他 goroutine 对它进行读操作。 是否需要对这个变量进行加锁保护。

我觉得不同goroutine并发读写同一个变量, 需要加锁, 这应该是天经地义的常识。 但是这个 PullRequest 居然出乎意料的被作者反驳了。

作者的理由是: 只有一个 goroutine 在写,其他 goroutine 在读,不需要加锁。

但是这样的观点我实在无法苟同, 因为在我的 C/C++ 开发经验中,这是必然需要加锁的典型场景,一般是使用读写锁。 难道是golang在这个方面有一些牛逼的奇淫巧计所以不需要加锁?

所以我耐不住寂寞开始查阅资料,先是官网上的说法。

go内存模型回顾

这个问题先让我们回顾一下 golang 官网上对于 go 内存模型的建议

Advice Programs that modify data being simultaneously accessed by multiple goroutines must serialize such access. To serialize access, protect the data with channel operations or other synchronization primitives such as those in the sync and sync/atomic packages. If you must read the rest of this document to understand the behavior of your program, you are being too clever. Don't be clever.

可以看出其实go的内存模型对于并发安全有两种保护措施。 一种是通过加锁来保护,另一种是通过channel来保护。 前者没什么好说的, 后者其实就是一个线程安全的队列。 在 C/C++ 在多线程编程中经常使用的 BlockingQueue , 几乎每个开源项目都有自己的 BlockingQueue, 其实实现起来并不难,大部分实现都大同小异, 我在自己的常用库里面也自己实现了一个BlockingQueue 。 内部加锁实现。

可能很多人都听说过一个高逼格的词叫【无锁队列】。 都一听到加锁就觉得很low,其实对于大部分程序来说。 根本不需要那些高逼格的技术,该加锁就加锁。 一次加锁的耗时差不多是在几十纳秒, 而一次网络IO都是在毫秒级别以上的。 根本不是一个量级。 特别是在现在云计算时代, 大部分人一辈子都遇不到因为加锁成为性能瓶颈的应用场景。

而且我觉得上面那段Advice里面写意味深长的是最后一句【Don't be clever】, 不要自作聪明。

也就是我的解读是:

go语言编程中, 当有多个goroutine并发操作同一个变量时,除非是全都是只读操作, 否则就得【加锁】或者【使用channel】来保证并发安全。 不要觉得加锁麻烦,但是它能保证并发安全。

不过在 Weed-FS-PullRequest-75 上还是说服不了作者, 所以最后在 go-nuts 邮件群组上面发起了这个话题。

go-nuts 上的讨论

go-nuts 上的讨论,打开链接。 经过讨论后,验证了我的观点是正确的, 所以作者也放心的 Merge 了我的建议 Weed-FS-PullRequest-79

摘出 go-nuts 上有个关于并发安全问题的好文章:Benign data races: what could possibly go wrong? 。 还有就是

代码语言:javascript
复制
go run/build/test -race

这个命令可以更大概率的复现并发安全问题。 有时候并发安全问题不容易复现。所以即使程序运行正常, 也不能说明就没有并发安全问题。

这让我想起来当时看到酷壳一篇博文疫苗:Java HashMap的死循环的时候, 我心里还嘲笑淘宝的人这么搓, 连非线程安全需要加锁都不知道。

没想到原来这么多人对线程安全(在go里面通常叫并发安全)没有清醒的认识, 不只是这个项目里面,包括在相关技术群里面讨论这个问题的时候, 总是有人以为每次读取数据都是原子行为(我怀疑他们可能不理解原子行为是什么), 以为不加锁导致的问题顶多就是读取的数据是修改前的数据罢了。 其实都是典型的误解。

本文来自:不是我干的

感谢作者:YanyiWu

查看原文:谈谈go语言编程的并发安全

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2017-04-13,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 Golang语言社区 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 问题起因
  • go内存模型回顾
  • go-nuts 上的讨论
相关产品与服务
对象存储
对象存储(Cloud Object Storage,COS)是由腾讯云推出的无目录层次结构、无数据格式限制,可容纳海量数据且支持 HTTP/HTTPS 协议访问的分布式存储服务。腾讯云 COS 的存储桶空间无容量上限,无需分区管理,适用于 CDN 数据分发、数据万象处理或大数据计算与分析的数据湖等多种场景。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档