如何防止递归函数中的死锁?

问题描述 投票:1回答:1

我有一个递归函数,我将锁定并改变其内部状态,但是,它将导致死锁。如何实现这种没有死锁的递归函数?

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!
go deadlock
1个回答
1
投票

此类问题的典型解决方案是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(),没有未完成的锁定会导致死锁。


0
投票

此类问题的典型解决方案是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(),没有未完成的锁定会导致死锁。

© www.soinside.com 2019 - 2024. All rights reserved.