Go's sync套餐有一个Mutex.不幸的是,它不是递归的.在Go中实现递归锁的最佳方法是什么?

推荐答案

很抱歉没有直接回答您的问题:

IMHO,在Go中实现递归锁的最佳方法是不实现它们,而是重新设计代码,使其一开始就不需要它们.我认为,对它们的渴望很可能表明对某些(此处未知)问题采用了错误的方法.

作为上述主张的间接"证据":对于涉及互斥锁的一些常见情况,递归锁是不是一种常见的/正确的方法,它迟早会包含在标准库中.

最后,最后但并非最不重要的一点:Go开发团队的罗斯 Cox在这里写道:

递归(也称为可重入)互斥锁不是一个好主意. 使用互斥锁的根本原因是互斥锁 保护不变量,可能是内部不变量,如 "p.Prev.Next==p表示环的所有元素",或者可能 外部不变量,如"我的局部变量x等于p.Prev".

锁定互斥锁断言"我需要保持不变量" 也许"我会暂时打破这些不变量." 释放互斥声明"我不再依赖那些 不变量"和"如果我 destruct 了它们,我就恢复了它们."

了解互斥锁保护不变量对于 标识哪里需要互斥锁,哪里不需要. 例如,共享计数器是否使用ATOM更新 递增和递减指令需要互斥锁吗? 这取决于不变量.如果唯一不变的是 计数器在i递增和d递减之后具有值i-d, 那么说明书的气势性就能确保 不变量;不需要互斥锁.但如果柜台一定要 与某些其他数据 struct 同步(也许它也算 列表上的元素数量),然后是 单打独斗是不够的.一些其他的东西, 通常是互斥体,必须保护更高级别的不变量. 这就是为什么Go中的 map 操作不是 保证是原子的:它会增加费用,而不是 在典型 case 中受益.

让我们来看看递归互斥锁. 假设我们有如下代码:

     func F() {
             mu.Lock()
             ... do some stuff ...
             G()
             ... do some more stuff ...
             mu.Unlock()
     }

     func G() {
             mu.Lock()
             ... do some stuff ...
             mu.Unlock()
     }

通常情况下,打电话给mu时.Lock返回调用代码

递归互斥实现将使G的mu.锁

递归互斥锁不保护不变量. 互斥锁只有一个任务,而递归互斥锁不做这件事.

还有一些更简单的问题,比如如果你写

     func F() {
             mu.Lock()
             ... do some stuff
     }

您永远不会在单线程测试中找到错误. 但这只是更大问题的特例, 那就是他们根本不能保证 互斥锁要保护的不变量.

如果您需要实现可以调用的功能 不管是否持有互斥体,要做的最清楚的事情就是 就是写两个版本.例如,代替上面的G, 您可以这样写:

     // To be called with mu already held.
     // Caller must be careful to ensure that ...
     func g() {
             ... do some stuff ...
     }

     func G() {
             mu.Lock()
             g()
             mu.Unlock()
     }

或者如果它们都是未出口的,g和glock.

我确信我们最终会需要TryLock;请随意 请为那件事给我们寄一个提货箱.超时锁定似乎不那么重要 但是如果有一个干净的实现(我不知道有一个) 那也许就没问题了.请不要寄出这样的提单. 实现递归互斥锁.

递归互斥只是一个错误,只不过

罗斯

Go相关问答推荐

有没有更简单的方法在Go中编写这个逻辑?

Go汇编器命名为Constants

文件路径.Abs()未在结果中提供子目录

Golang Gorm Fiber / argon2.Config 未定义

Fizz对Gin的OpenAPI生成器正在重命名类型

正确的 shell 程序进入 golang alpine docker 容器的入口点?

在 Cloud Run 中找不到默认凭据

如何使用 GolangCI 删除未使用的导入

使用 unsafe.Pointer 将 struct point直接转换为另一个 struct 是否安全?

Get 请求在 Thunder 客户端/Postman 中返回数据,但在 Golang 代码中给出空白数据

如何在模板中传递和访问 struct 片段和 struct

Go Colly 如何找到请求的元素?

Go 信号处理程序不处理终端窗口关闭

如果 transaction.Commit 对带有 postgres 连接的 SQL 失败,您是否需要调用 transaction.RollBack

Golang - 客户 Unmarshaler/Marshaler 在指针上具有 nil/null 值

io.Pipe 使用困难

转到文本/模板模板:如何根据模板本身的值数组判断值?

为什么import和ImportSpec之间可以出现多行注释,而PackageName和ImportPath之间不能出现?

如何从 Go 1.18 中的单个方法返回两种不同的具体类型?

如何获得Ent中数字列的总和