我有一个递归函数,我将锁定并改变其内部状态,但是,它将导致死锁。如何实现这种没有死锁的递归函数?
package main
import (
"fmt"
"sync"
)
type runner struct {
sync.Mutex
value int
}
func (r *runner) decrement() {
r.Lock()
defer r.Unlock()
if r.value == 0 {
return
}
r.value--
r.decrement()
}
func main() {
r := runner{
value: 10,
}
r.decrement()
fmt.Printf("r: %v\n", r.value)
}
期望运行上面的代码会显示r: 0
,但实际上出现了死锁:
fatal error: all goroutines are asleep - deadlock!
此类问题的典型解决方案是reentrant mutex。但是,Go团队的Russ Cox对于reentrant mutexes are a bad idea为什么有一个很好的论点。
在这种情况下,您不想推迟解锁。相反,您应该锁定和解锁必要的最小部分。
func (r *runner) decrement() {
r.Lock()
if r.value == 0 {
r.Unlock()
return
}
r.value--
r.Unlock()
r.decrement()
}
这解决了两个问题:一,它通过不对不需要锁定的事物进行锁定,从而(稍微)提高了代码并发运行的能力;二,它确保了您重新输入decrement()
,没有未完成的锁定会导致死锁。
此类问题的典型解决方案是reentrant mutex。但是,Go团队的Russ Cox对于reentrant mutexes are a bad idea为什么有一个很好的论点。
在这种情况下,您不想推迟解锁。相反,您应该锁定和解锁必要的最小部分。
func (r *runner) decrement() {
if r.value == 0 {
return
}
r.Lock()
r.value--
r.Unlock()
r.decrement()
}
这解决了两个问题:其一,它通过不为不需要锁定的事物获取锁而(略微地)提高了代码并发运行的能力;其二,它确保了如果您重新输入decrement()
,没有未完成的锁定会导致死锁。