Go中的递归锁定
Go的Go中的递归锁定,go,Go,Go的sync软件包有一个互斥锁。不幸的是,它不是递归的。在Go中实现递归锁的最佳方法是什么?很抱歉,我没有直接回答您的问题: IMHO,在Go中实现递归锁的最佳方法是不实现它们,而是重新设计代码,使其在一开始就不需要它们。我认为,对它们的渴望很可能表明对某些(此处未知)问题采用了错误的方法 作为上述声明的间接“证明”:递归锁是否是处理涉及互斥锁的常见/正确方法,它迟早会包含在标准库中 最后,最后但并非最不重要的一点:Go开发团队的Russ Cox在这里写道: 递归(也称为可重入)互斥是个坏主意
sync
软件包有一个互斥锁
。不幸的是,它不是递归的。在Go中实现递归锁的最佳方法是什么?很抱歉,我没有直接回答您的问题:
IMHO,在Go中实现递归锁的最佳方法是不实现它们,而是重新设计代码,使其在一开始就不需要它们。我认为,对它们的渴望很可能表明对某些(此处未知)问题采用了错误的方法
作为上述声明的间接“证明”:递归锁是否是处理涉及互斥锁的常见/正确方法,它迟早会包含在标准库中
最后,最后但并非最不重要的一点:Go开发团队的Russ Cox在这里写道:
递归(也称为可重入)互斥是个坏主意。
使用互斥体的根本原因是互斥体
保护不变量,可能是内部不变量,比如
“p.Prev.Next==p表示环的所有元素”,或者
外部不变量,如“我的局部变量x等于p.Prev”
锁定互斥锁声明“我需要保持不变量”
也许“我会暂时打破那些不变量。”
释放互斥锁声明“我不再依赖于这些
“不变量”和“如果我弄坏了它们,我已经恢复了它们。”
理解互斥体保护不变量对于
确定哪里需要互斥锁,哪里不需要互斥锁。
例如,是否使用原子更新共享计数器
递增和递减指令需要互斥锁吗?
这取决于不变量。如果唯一不变的是
计数器在i递增和d递减后具有值i-d,
然后,说明的灵活性确保了
不变量;不需要互斥。但是,如果必须使用计数器
与其他一些数据结构同步(可能很重要
列表中的元素数),然后是
单个操作是不够的。还有一件事,
通常是互斥,必须保护较高级别的不变量。
这就是Go中地图操作不可用的原因
保证是原子的:它会增加费用,而不会
在典型案例中受益
让我们看看递归互斥体。
假设我们有这样的代码:
通常,当对mu.Lock的调用返回时,调用代码
现在可以假定受保护的不变量保持不变,直到
它叫mu.Unlock
递归互斥实现将使G的mu.Lock
和mu.Unlock调用在F内调用时是无操作的
或者当前线程已经持有mu的任何其他上下文。
如果mu使用了这样的实现,那么当mu.Lock
返回G中的不变量,不变量可能成立,也可能不成立。视情况而定
F在打电话给G之前做了些什么也许F根本没有意识到
G需要这些不变量,并且(完全)破坏了它们
可能的,尤其是在复杂代码中)
递归互斥体不保护不变量。
互斥体只有一个任务,而递归互斥体则没有
它们有一些更简单的问题,比如如果你写了
你永远不会在单线程测试中发现这个bug。
但这只是更大问题的一个特例,
也就是说他们根本不保证
互斥体要保护的不变量
如果您需要实现可调用的功能
无论是否持有互斥,最清晰的做法
就是写两个版本。例如,不是上面的G,
你可以写:
或者如果它们都没有被报告,g和gLocked
我相信我们最终会需要特里洛克;随便
给我们寄一张信用证。带超时的锁似乎不那么重要
但是如果有一个干净的实现(我不知道有)
那也许没关系。请不要发送这样的CL
实现递归互斥
递归互斥只是一个错误,只不过
虫子舒适的家
罗斯
您可以很容易地对和进行递归锁定。请参阅以获取一些想法 除了Go运行时没有公开goroutine Id的任何概念。这是为了阻止人们使用goroutine本地存储做愚蠢的事情,并且可能表明设计者认为如果你需要goroutine Id,你就错了
如果你真的想,当然可以。你可能想读一下这篇文章,看看为什么Go的设计者认为这是一个坏主意。正如已经确定的那样,从并发的角度来看,这是一个悲惨、可怕、可怕的想法 无论如何,由于您的问题实际上是关于Go的类型系统,下面是如何使用递归方法定义类型
type Foo struct{}
func (f Foo) Bar() { fmt.Println("bar") }
type FooChain struct {
Foo
child *FooChain
}
func (f FooChain) Bar() {
if f.child != nil {
f.child.Bar()
}
f.Foo.Bar()
}
func main() {
fmt.Println("no children")
f := new(FooChain)
f.Bar()
for i := 0; i < 10; i++ {
f = &FooChain{Foo{}, f}
}
fmt.Println("with children")
f.Bar()
}
type Foo struct{}
func(f Foo)Bar(){fmt.Println(“Bar”)}
类型FooChain结构{
福
儿童食物链
}
func(f FooChain)Bar(){
如果f.child!=nil{
f、 child.Bar()
}
f、 Foo.Bar()
}
func main(){
fmt.Println(“无子女”)
f:=新(食物链)
f、 Bar()
对于i:=0;i<10;i++{
f=&FooChain{Foo{},f}
}
fmt.Println(“有子女”)
f、 Bar()
}
。他不喜欢他们!我创建了一个允许计数/递归锁的库。试一试:github.com/jwells131313/goetheI猜想所有支持递归锁的语言(其中大多数)都是糟糕的、可怕的、可怕的。不一定,只是在Go中管理并发性很糟糕。您试图解决的问题是什么?@ErikAigner锁通常比其他并行设计构造慢。另一种模式(当需要提供同步时)可能是使用Go通道。你的“证据”回避了一个问题:Erik有一个递归锁定可能解决的问题,但它在标准库中还不可用。我们不需要添加支持,因为如果需要,它会
func F() {
mu.Lock()
... do some stuff
}
// 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()
}
type Foo struct{}
func (f Foo) Bar() { fmt.Println("bar") }
type FooChain struct {
Foo
child *FooChain
}
func (f FooChain) Bar() {
if f.child != nil {
f.child.Bar()
}
f.Foo.Bar()
}
func main() {
fmt.Println("no children")
f := new(FooChain)
f.Bar()
for i := 0; i < 10; i++ {
f = &FooChain{Foo{}, f}
}
fmt.Println("with children")
f.Bar()
}