Go1.18 新特性:被折腾 N 次的 TryLock

大家好,我是煎鱼。

还记得我之前写过一篇《Go 为什么不支持可重入锁?》的文章,主要是介绍有其他语言经验的小伙伴,想要 Go 支持可重入锁,被残忍拒绝的历程。

189a3043936daa48985f0b0b942e07a9.png
来自《Go 为什么不支持可重入锁?》

大佬总会有拗不过的时候,虽然不是可重入锁。但在 Go1.18 实现了尝试获取锁(TryLock)的新方法,也有点那味道了。

今天煎鱼带大家学习被折腾了 3 回的 “他”。

背景

提新功能,肯定涉及用户场景,时间来到 2018 年,@deanveloper 就提了一个经典的加载场景:需加载几个非常大的文件,想要一个进度条来显示我离完成的时间有多远。

他考虑这个进度条可以很好地利用 TryLock 来实现,以下为他的示例代码:

func (b *ProgressBar) Add(n int) {
    atomic.AddInt64(&b.Progress, int64(n))

    if b.Progress >= b.Max {
        b.once.Do(b.updateClientsDone)
        return
    }

    if b.pctMx.TryLock() {
        defer b.pctMx.Unlock()
        b.updateClients()
    }
}

上述代码的基本逻辑,就是不断地更新计数器,接着通过尝试获取锁,来实现他的滚动加载进度条。

由于大佬们认为他这个用 channel+select-default 来做,会更好,这个用户案例不够足矣支撑 TryLock 的功能增加,再度被拒绝。

再战

经过 2013、2018 年的几次大量讨论,时间再度来到 2021 年,@TyeMcQueen 举例了大量 h2 库的一些例子,表示有 TryLock 方法会更好的一些期许。

但也被拒绝了,Russ Cox 反对的原因是:

dbfc0b940cc4801fe7de73c9079bbef9.png
  • 互斥锁是用来保护不变量的。如果锁被别人持有,你就没有什么可以说的不变量了。

  • TryLock 方法鼓励对锁进行不精确的思考;它鼓励对不变量进行假设,这些假设可能是真的,也可能不是真的。这最终成为了它自己的竞赛来源。

翻身

在前几次失败的案例中,Russ Cox 认为给出的案例并不足以说服作为 TryLock 系列方法添加的理由。

认为需要添加的人越来越多,加上后面 Google 的大佬 Dmitry Vyukov 给出了如下案例:

935585d9908db7569620e98db59666eb.png

表示像是 gvisor、v.io/x/lib/nsync、trivago/tgo 等软件库都有使用到 TryLock 的这类方法,实现与模拟代码基本一致。

72109f04825be1e468a4a121607ff001.png

最终 Russ Cox 松口,表示:“大家都同意这是不幸的,但有时是必要的”,感觉勉强答应了。

8f1ea0e6b361ae24f1a02150fa82bb83.png

考虑的是是给一个官方实现,而非出现各种第三方 TryLock 方法,显得就很低效,重复实现了。

历史的整体时间线历程如下:

  • 2013 年 @lukescott 提出《sync: mutex.TryLock》,被拒绝。

  • 2018 年 @deanveloper 提出《proposal: add sync.Mutex.TryLock》,被拒绝。

  • 2021 年 @TyeMcQueen 提出《sync: add Mutex.TryLock》,先被拒绝,后接受。

  • 2022 年,由于之前 Go1.17 功能特性已冻结,定在 Go1.18 发布(3 月)。

新方法 sync.TryLock

在即将发布的 Go1.18 中,主要是在 sync 标准库中新增了 TryLock 系列的相关方法。

如下图:

af76cf43e7a678de5b9f34d0f33addbd.png
sync@master#Mutex.TryLock
  • Mutex.TryLock:尝试锁定互斥锁,返回是否成功。

  • RWMutex.TryLock:尝试锁定读写锁,返回是否成功。

  • RWMutex.TryRLock。尝试锁定读锁,返回是否成功。

官方特意提醒:虽然使用 TryLock 的场景确实存在。但应该是罕见的,使用 TryLock 往往可能是更深层次问题的标志。

总结

在 Go1.18,尝试获取锁的 TryLock 方法终于落地了,该方法的存在有利有弊。像是以后可能就会成为一个 if-else 常用的判断了,也能规避掉不少锁阻塞导致的长时间 hold 住。

但从应用程序设计上来讲,该方法的使用,有的就是有问题的,需要特别的关注和思考。

不容易啊,历经 9 年。

关注煎鱼,获取业内第一手消息和知识 👇

ee138e008fcab5fc9303f7b7b3d84415.png

13914778585bc501eab0aa04befcfc69.png

你好,我是煎鱼,出版过 Go 畅销书《Go 语言编程之旅》,再到获得 GOP(Go 领域最有观点专家)荣誉,点击蓝字查看我的出书之路

日常分享高质量文章,输出 Go 面试、工作经验、架构设计,加微信拉读者交流群,和大家交流!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值