Go的sync
包裹中有个Mutex
。不幸的是,它不是递归的。在Go中实现递归锁的最佳方法是什么?
很抱歉没有直接回答您的问题:
恕我直言,在Go中实现递归锁的最好方法是不实现它们,而是重新设计代码以一开始就不需要它们。我认为,对他们的渴望可能表明对某些(此处未知)问题使用了错误的方法。
作为上述权利要求的间接“证明”:递归锁是否是对涉及互斥锁的某些常见情况的一种常见/正确方法,它迟早会包含在标准库中。
最后,最后但并非最不重要的一点:Go开发团队的Russ Cox在这里https://groups.google.com/d/msg/golang-
nuts/XqW1qcuZgKg/Ui3nQkeLV80J
写道:
递归(又称可重入)互斥是一个坏主意。使用互斥锁的根本原因是互斥锁保护不变式,也许是内部不变式,例如“ p.Prev.Next ==
p代表环的所有元素”,或者是外部不变式,例如“我的局部变量x等于p.Prev ”。锁定互斥锁会断言“我需要保持不变式”,并且也许“我会暂时破坏这些不变式”。释放互斥量会断言“我不再依赖那些不变式”和“如果我打破了它们,我就将它们还原了”。
了解互斥体保护不变式对于确定需要互斥体和不需要互斥体至关重要。例如,用原子增量和减量指令更新的共享计数器是否需要互斥锁?这取决于不变性。如果唯一不变的是在i递增和d递减后计数器具有值i-d,则指令的气氛确保不变。不需要互斥。但是,如果计数器必须与其他数据结构同步(也许它对列表中元素的数量进行计数),则单个操作的原子性是不够的。通常是互斥体的其他东西必须保护更高级别的不变式。这就是不能保证Go中的地图操作是原子的原因:在典型情况下,这样做会增加费用而没有收益。
让我们看一下递归互斥体。假设我们有如下代码:
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的调用时,调用代码现在可以假定受保护的不变式成立,直到调用mu.Unlock为止。
从F或当前线程已经拥有mu的任何其他上下文中调用时,递归互斥体实现将使G的mu.Lock和mu.Unlock调用成为no-
ops。如果mu使用了这样的实现,则当mu.Lock返回G内部时,不变量可能成立,也可能不成立。这取决于F在调用G之前所做的事情。也许F甚至没有意识到G需要这些不变式并且已经破坏了它们(完全有可能,尤其是在复杂代码中)。递归互斥不能保护不变式。互斥锁只有一项工作,而递归互斥锁则不行。
它们有更简单的问题,例如您写的
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和gLocked。
我确信我们最终将需要TryLock;请随时向我们发送一份CL。用超时锁定似乎不是很重要,但是如果有一个干净的实现(我不知道一个实现),那可能就可以了。请不要发送实现递归互斥的CL。
递归互斥体只是一个错误,无非就是一个舒适的错误之家。
拉斯
Go语言支持递归函数,这里是一个经典的斐波拉切数列的列子。 package main import "fmt" // fact函数不断地调用自身,直到达到基本状态fact(0) func fact(n int) int { if n == 0 { return 1 } return n * fact(n-1) } func main() { fmt.
问题内容: 我是Go编程语言的新手,我有一个创建和解释器的任务,但是我遇到了以下问题: 我想将环境定义为: 但是我收到错误“无效的递归类型环境”。根据我的研究,我将父级更改为 Environment。但是现在,当我需要创建一个环境类型为var的新环境时,它会收到错误消息“无法将fun_Val.ds(环境类型)用作 环境值类型”。我正在如下创建环境: 我试图将这篇文章中的代码量限制在一定范围内,但是
主要内容:斐波那契数列,数字阶乘,多个函数组成递归很对编程语言都支持递归函数,Go语言也不例外,所谓递归函数指的是在函数内部调用函数自身的函数,从数学解题思路来说,递归就是把一个大问题拆分成多个小问题,再各个击破,在实际开发过程中,递归函数可以解决许多数学问题,如计算给定数字阶乘、产生斐波系列等。 构成递归需要具备以下条件: 一个问题可以被拆分成多个子问题; 拆分前的原问题与拆分后的子问题除了数据规模不同,但处理问题的思路是一样的; 不能无限制的
递归,就是在运行的过程中调用自己。 语法格式如下: func recursion() { recursion() /* 函数调用自身 */ } func main() { recursion() } Go 语言支持递归。但我们在使用递归时,开发者需要设置退出条件,否则递归将陷入无限循环中。 递归函数对于解决数学上的问题是非常有用的,就像计算阶乘,生成斐波那契数列等。 阶乘 以下实
问题内容: 我正在尝试在Go中的另一个函数中定义一个递归函数,但是我在努力获取正确的语法。我正在寻找这样的东西: 我想将Function2保留在Function1的范围内,因为它正在访问其范围的某些元素。 如何在Go中执行此操作? 非常感谢 问题答案: 如果它在声明它的行中,则无法访问它的内部。原因是您不是在指 函数, 而是指 变量 (类型是函数),并且只有在声明后才能访问它。 引用规格:声明和范
问题内容: 由于以下代码中的某种原因,我收到“致命错误:所有goroutine都在睡眠-死锁!”。我正在使用应该不阻塞的缓冲通道。不知道我在做什么错 游乐场链接https://play.golang.org/p/J9meD5aKna 问题答案: 虽然您的解决方案可能会起作用,但我对此并不满意。 首先,您需要更改通道大小以使其起作用这一事实表明存在潜在的问题/错误。现在,每次您要启动另一个时,都必须