当前位置: 首页 > 后端技术 > PHP

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

时间:2023-03-29 21:34:13 PHP

大家好,我是炸鱼。程序中的锁是很多小伙伴在编写分布式应用时使用的最强大的工具之一。使用Go的同学大多都有其他语言的使用经验,会对其中一个疑惑,那就是Go中的锁不支持重入?为此,今天建宇就带大家了解一下这里的设计注意事项,看看为什么。如果可重入锁对一个已经锁定的普通互斥量执行“锁定”操作,结果要么失败,要么被阻塞,直到解锁。加锁场景如下:加锁时:如果是可重入互斥量,如果当前尝试加锁的线程是持有锁的线程,加锁操作就会成功。在解锁方面:可重入互斥锁一般会记录自己被加锁的次数,只有执行了相同次数的解锁操作才会真正解锁。简单的说,可重入互斥量就是互斥量的一种,同一个线程对其多次加锁,不会出现死锁或阻塞。不同语言的实现可能或多或少不同,但大体意思是相似的。请大家想一想,Go是什么样的?Go支持我们看到下面的Gomutex示例:varmusync.Mutexfuncmain(){mu.Lock()mu.Lock()}这个Go程序会阻塞吗?不行,会报如下错误:fatalerror:allgoroutinesareasleep-deadlock!Go显然不支持可重入互斥量。官方回复Go设计原则在工程中使用互斥的根本原因是:为了保护不变量,也可以用来保护内部和外部不变量。基于此,Go在互斥锁的设计上也会遵循这些原则。如下:在调用mutex.Lock方法时,需要保证这些变量的不变性保持不变,不会在后续过程中被破坏。在调用mu.Unlock方法时,确保程序不再需要依赖那些不变量。如果您的程序在锁定时销毁了互斥体,您需要确保已恢复它们。不支持的原因说完Go本身的设计原则,为什么不支持重入呢?其实RussCox在2010年的《Experimenting with GO》给出了一个答案,认为递归(aka:reentrant)互斥是个坏主意,这样的设计不好。我们可以通过官方例子来理解。如下:funcF(){mu.Lock()...做一些事情...G()...做更多事情...mu.Unlock()}funcG(){mu.Lock()...dosomestuff...mu.Unlock()}在上面的代码中,我们在F方法中调用了mu.Lock方法来加锁。如果支持可重入锁,那么就会进入G方法。这时候就会出现一个致命的问题。你不知道F和G方法加锁后是不是做了什么,导致了不变量的破坏。毕竟开几个协程干坏事是完全有可能的。这对于Go来说是不可接受的。可重入设计违背了上面提到的设计理念,即:“要确保这些变量的不变性保持不变,不会在后续过程中被破坏”。基于以上原因,Go官方团队选择不支持该特性。综上所述,Gomutex不支持可重入锁的设计,我喜欢简单明了的想法。可能干扰比较多,还是直接干脆的好。您在工作过程中是否也有类似的疑惑?欢迎在评论区留言交流:)如有任何问题,欢迎在评论区反馈交流。最好的关系是相互成就。您的点赞是建宇创造的巨大动力,感谢您的支持。文章持续更新中。可以微信搜索【脑补炸鱼】阅读。本文已收录在GitHubgithub.com/eddycjy/blog中。学习Go语言可以看Go学习地图和路线。欢迎星星提醒。