Go 为什么不支持可重入锁?

开发 后端
使用 Go 的同学里,绝大部分都有其他语言的经验,就会对其中一点有疑惑,那就是 Go 里的锁,竟然不支持可重入?

[[440472]]

本文转载自微信公众号「脑子进煎鱼了」,作者陈煎鱼。转载本文请联系脑子进煎鱼了公众号。

大家好,我是煎鱼。

程序里的锁,是很多小伙伴在写分布式应用时用的最多的一个利器之一。

使用 Go 的同学里,绝大部分都有其他语言的经验,就会对其中一点有疑惑,那就是 Go 里的锁,竟然不支持可重入?

为此,今天煎鱼带大家一起来了解这里的设计考量,看看为什么。

可重入锁

如果对已经上锁的普通互斥锁进行 “加锁” 操作,其结果要么失败,要么会阻塞至解锁。

锁的场景如下:

  • 在加锁上:如果是可重入互斥锁,当前尝试加锁的线程如果就是持有该锁的线程时,加锁操作就会成功。
  • 在解锁上:可重入互斥锁一般都会记录被加锁的次数,只有执行相同次数的解锁操作才会真正解锁。

简单来讲,可重入互斥锁是互斥锁的一种,同一线程对其多次加锁不会产生死锁,又或是导致阻塞。

不同语言间实现可能或多或少有些区别,但大体意思差不多。

请你想一下,Go 是怎么样的呢?

Go 支持情况

我们看到以下这个 Go 互斥锁例子:

  1. var mu sync.Mutex 
  2.  
  3. func main() { 
  4.  mu.Lock() 
  5.  mu.Lock() 

这段 Go 程序会阻塞吗?不会,会报以下错误:

  1. fatal error: all goroutines are asleep - deadlock! 

Go 显然是不支持可重入互斥锁的。

官方回复

Go 设计原则

在工程中使用互斥的根本原因是:为了保护不变量,也可以用于保护内、外部的不变量。

基于此,Go 在互斥锁设计上会遵守这几个原则。如下:

  • 在调用 mutex.Lock 方法时,要保证这些变量的不变性保持,不会在后续的过程中被破坏。
  • 在调用 mu.Unlock 方法时,要保证:
    • 程序不再需要依赖那些不变量。
    • 如果程序在互斥锁加锁期间破坏了它们,则需要确保已经恢复了它们。

不支持的原因

讲了 Go 自己的设计原则后,那为什么不支持可重入呢?

其实 Russ Cox 于 2010 年在《Experimenting with GO[1]》就给出了答复,认为递归(又称:重入)互斥是个坏主意,这个设计并不好。

我们可以结合官方的例子来理解。

如下:

  1. func F() { 
  2.         mu.Lock() 
  3.         ... do some stuff ... 
  4.         G() 
  5.         ... do some more stuff ... 
  6.         mu.Unlock() 
  7.  
  8. func G() { 
  9.         mu.Lock() 
  10.         ... do some stuff ... 
  11.         mu.Unlock() 

在上述代码中,我们在 F 方法中调用 mu.Lock 方法加上了锁。如果支持可重入锁,接着就会进入到 G 方法中。

此时就会有一个致命的问题,你不知道 F 和 G 方法加锁后是不是做了什么事情,从而导致破坏了不变量,毕竟随手起几个协程做点坏事,也是完全可能的。

这对于 Go 是无法接受的,可重入的设计违反了前面所提到的设计理念,也就是:“要保证这些变量的不变性保持,不会在后续的过程中被破坏”。

基于上述原因,Go 官方团队选择了没有支持该项特性。

总结

Go 互斥锁没有支持可重入锁的设计,也是喜欢的大道至简的思路了,可能的干扰比较多,不如直接简单的来。

你在工作过程中有没有类似的疑惑呢,欢迎大家在评论区留言和交流:)

参考资料

[1]Experimenting with GO: https://groups.google.com/g/golang-nuts/c/XqW1qcuZgKg/m/Ui3nQkeLV80J

 

责任编辑:武晓燕 来源: 脑子进煎鱼了
相关推荐

2021-10-27 07:15:36

Go 循环引用

2021-12-09 10:51:47

Go继承

2024-01-01 08:10:40

Go语言map

2024-01-05 08:45:35

Go语言map

2023-01-28 08:05:32

转换Go泛型

2021-11-08 11:02:01

Go函数重载

2023-02-26 23:36:08

PHPGo函数

2024-03-08 08:51:59

Gomain函数

2020-10-09 06:48:19

Pythonswitch语句

2020-07-22 08:01:41

Python开发运算符

2024-03-12 09:13:28

Go语言main

2021-02-01 13:53:53

StringlongJava

2021-06-11 00:03:31

鸿蒙智能手机

2009-03-12 08:42:38

AndroidWMMTK

2021-08-02 09:31:20

Python工具代码

2021-07-13 08:09:34

微博推特评论

2023-04-03 11:21:29

PythonGoRust

2009-03-11 17:32:22

联发科WMAndroid

2014-06-05 15:16:49

Linux开源Flash

2011-12-09 20:28:50

点赞
收藏

51CTO技术栈公众号