Go中的递归关键部分

时间:2015-05-26 22:11:06

标签: recursion concurrency go mutex

据我所知,Go中没有对递归互斥体的支持(并且很多人都认为这些是危险的),并且这些通道是实现复杂并发模式的首选方式。

但是,我无法找出任何合理的方法来实现一个非常常见的并发模式 - 重入或递归的关键部分。

粗略地说:goroutines A和B将争夺关键部分的锁定(假设结构中的某些状态需要进行原子修改)。让我们说A收到锁。但是,A会递归,可能需要多次进入临界区。当它进入临界区时,goroutine B将获得锁定等。

我想用通道实现这个(或者在Go中以任何可能的方式实现),而不必在可能通过临界区的函数的整个调用树中来回传递一些字符串或标记(没有“goroutine id”可用)。并且不需要使用runtime包进行杂乱/昂贵的堆栈内省。

如何做到这一点?

1 个答案:

答案 0 :(得分:1)

说,上面的例子如下:

package main

import "fmt"

type Foo struct {
    // here must be reentrant mutex
    Value int
}

var F Foo

func A() {
    F.Value += 1
    if F.Value < 10 {
        A()
    }
}

func B() {
    F.Value += 5
    if F.Value < 20 {
        A()
    }
}

func main() {
    F = Foo{
        Value: 0,
    }
    A()
    B()
    fmt.Println("F is", F.Value)
}

http://play.golang.org/p/STnaLUCaqP

然后在通道中实现应该遵循简单的原则 - 只有一个地方可以读取或写入F.Value,由 select 语句包装。像这样:

package main

import "fmt"

type Foo struct {
    Value int
}

var (
    F  Foo
    ch = make(chan int)
)

func A() {
    val := <-ch
    ch <- val+1
    if val < 10 {
        A()
    }
}

func B() {
    val := <-ch
    ch <- val+5
    if val < 20 {
        A()
    }
}

func main() {
    F = Foo{
        Value: 0,
    }
    go func() {
        for {
            select {
            case val := <-ch:
                F.Value = val
            case ch <- F.Value:
            }
        }
    }()
    A()
    B()
    fmt.Println("F is", F.Value)
}

http://play.golang.org/p/e5M4vTeet2

这里我们使用双向缓冲通道来获取/设置F.Value。一个读者,一个作家,选择会处理访问权限。

您可能也会对可重入互斥锁上的golang-nuts相关主题感兴趣:https://groups.google.com/forum/#!topic/golang-nuts/vN5ncBdtkcA有很好的解释为什么重入互斥锁在Go中没用(而且不是危险问题)。