这不会又是一个Go语言的bug吗?

2026-04-18 04:021阅读0评论SEO教程
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计855个文字,预计阅读时间需要4分钟。

这不会又是一个Go语言的bug吗?

您好,大家庭,我是小楼。最近我又双叒写了一个BUG,一个线上服务死锁了,不过幸好是个新服务,没有太大影响。问题是Go的读写锁,如果你是写Java的,不必慌张,更要看看。

hello,大家好呀,我是小楼。

最近我又双叒叕写了个BUG,一个线上服务死锁了,不过幸亏是个新服务,没有什么大影响。

出问题的是Go的读写锁,如果你是写Java的,不必划走,更要看看本文,本文的重点在于Java和Go的读写锁对比,甚至看完后你会有一个隐隐的感觉:Go的读写锁是不是有BUG?

故障回放

背景简单抽象一下:一个server服务(Go语言实现),提供了一个github.com/golang/go/issues/30657

从标题就能看出他遇到了和我一样的问题:

Read-locking shouldn't hang if thread has already a write-lock? #30657

看看里面有人是怎么回答的:

这位大佬说,这不符合Go锁的原理,Go的锁是不知道协程或者线程信息的,只知道代码调用先后顺序,即读写锁无法升级或降级。

Java中的锁记录了持有者(线程id),但Go的锁是不知道持有者是谁,所以获取了读锁之后再次获取读锁,这里的逻辑是区分不了是持有者还是其他的协程,所以就统一处理。

这点其实在Go源码的注释中体现了,我也是后来才注意到:

翻译一下是:

如果一个协程持有读锁,另一个协程可能会调用Lock加写锁,那么再也没有一个协程可以获得读锁,直到前一个读锁释放,这是为了禁止读锁递归。也确保了锁最终可用,一个阻塞的写锁调用会将新的读锁排除在外。

不过这个警示实在是太不起眼了,大概就是这个效果:

这一幕像极了产品和程序员:

  • 产品经理:我要实现这个功能,怎么实现我不管
  • Go:这破坏了我的设计原则,不接受这个功能
  • 产品经理:大家都退一步,你换个代价小的方法解决吧

于是,程序员在读写锁上写下了一段注释:

这不会又是一个Go语言的bug吗?

最后

这个死锁的坑确实很容易踩,尤其是Java程序员来写Go,所以我们写Go代码时还是得写得更Go一点才行。

Go的设计者比较「偏执」,认为「不好」的设计坚决不去实现,就如锁的实现不应该依赖线程、协程信息;可重入(递归)锁是一种不好的设计。所以这种看似有BUG的设计,也存在一定的道理

当然每个人都有自己的想法,你觉得Go的读写锁这样实现合理吗?

如果你看完觉得有点收获,给个在看吧,你的支持是我持续创作的动力~


搜索关注微信公众号"捉虫大师",后端技术分享,架构设计、性能优化、源码阅读、问题排查、踩坑实践。

本文共计855个文字,预计阅读时间需要4分钟。

这不会又是一个Go语言的bug吗?

您好,大家庭,我是小楼。最近我又双叒写了一个BUG,一个线上服务死锁了,不过幸好是个新服务,没有太大影响。问题是Go的读写锁,如果你是写Java的,不必慌张,更要看看。

hello,大家好呀,我是小楼。

最近我又双叒叕写了个BUG,一个线上服务死锁了,不过幸亏是个新服务,没有什么大影响。

出问题的是Go的读写锁,如果你是写Java的,不必划走,更要看看本文,本文的重点在于Java和Go的读写锁对比,甚至看完后你会有一个隐隐的感觉:Go的读写锁是不是有BUG?

故障回放

背景简单抽象一下:一个server服务(Go语言实现),提供了一个github.com/golang/go/issues/30657

从标题就能看出他遇到了和我一样的问题:

Read-locking shouldn't hang if thread has already a write-lock? #30657

看看里面有人是怎么回答的:

这位大佬说,这不符合Go锁的原理,Go的锁是不知道协程或者线程信息的,只知道代码调用先后顺序,即读写锁无法升级或降级。

Java中的锁记录了持有者(线程id),但Go的锁是不知道持有者是谁,所以获取了读锁之后再次获取读锁,这里的逻辑是区分不了是持有者还是其他的协程,所以就统一处理。

这点其实在Go源码的注释中体现了,我也是后来才注意到:

翻译一下是:

如果一个协程持有读锁,另一个协程可能会调用Lock加写锁,那么再也没有一个协程可以获得读锁,直到前一个读锁释放,这是为了禁止读锁递归。也确保了锁最终可用,一个阻塞的写锁调用会将新的读锁排除在外。

不过这个警示实在是太不起眼了,大概就是这个效果:

这一幕像极了产品和程序员:

  • 产品经理:我要实现这个功能,怎么实现我不管
  • Go:这破坏了我的设计原则,不接受这个功能
  • 产品经理:大家都退一步,你换个代价小的方法解决吧

于是,程序员在读写锁上写下了一段注释:

这不会又是一个Go语言的bug吗?

最后

这个死锁的坑确实很容易踩,尤其是Java程序员来写Go,所以我们写Go代码时还是得写得更Go一点才行。

Go的设计者比较「偏执」,认为「不好」的设计坚决不去实现,就如锁的实现不应该依赖线程、协程信息;可重入(递归)锁是一种不好的设计。所以这种看似有BUG的设计,也存在一定的道理

当然每个人都有自己的想法,你觉得Go的读写锁这样实现合理吗?

如果你看完觉得有点收获,给个在看吧,你的支持是我持续创作的动力~


搜索关注微信公众号"捉虫大师",后端技术分享,架构设计、性能优化、源码阅读、问题排查、踩坑实践。