dragon7088 2015-05-26 22:11
浏览 59

Go中的递归关键部分

I understand that there is no support for recursive mutexes in Go (and that a lot of folks consider these dangerous), and that channels are the preferred way to implement complex concurrency patterns.

However, I cannot figure out any sensible way to implement a very common concurrency pattern - that of the re-entrant or recursive critical section.

Roughly: goroutines A and B will compete for a lock over a critical section (say some state in a struct needs to be atomically modified). Let's say A receives the lock. However, A will recurse, possibly needing to enter the critical section many times. When it has exited the critical section as it has entered it, goroutine B will get the lock, etc.

I want to implement this with channels (or in any way possible in Go otherwise), without having to pass some string or token back and forth through the whole call tree of functions that might pass through the critical section (there is no "goroutine id" available)., and without having messy/expensive stack introspection necessary using runtime package.

How can this be done?

  • 写回答

1条回答 默认 最新

  • douzhao7445 2015-05-26 23:48
    关注

    Say, your example above looks like:

    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

    Then implementation in channels should follow simple principle - only one place where F.Value is read or written, wrapped by select statement. Something like this:

    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

    Here we use bidirectional buffered channel for getting/setting F.Value. One reader, one writer, select does all the magic to handling access.

    You may also be interested in relevant topic in golang-nuts on the reentrant mutexes: https://groups.google.com/forum/#!topic/golang-nuts/vN5ncBdtkcA There are good explanation why reentrant mutexes are not useful in Go (and it's not the question of danger).

    评论

报告相同问题?

悬赏问题

  • ¥15 拟通过pc下指令到安卓系统,如果追求响应速度,尽可能无延迟,是不是用安卓模拟器会优于实体的安卓手机?如果是,可以快多少毫秒?
  • ¥20 神经网络Sequential name=sequential, built=False
  • ¥16 Qphython 用xlrd读取excel报错
  • ¥15 单片机学习顺序问题!!
  • ¥15 ikuai客户端多拨vpn,重启总是有个别重拨不上
  • ¥20 关于#anlogic#sdram#的问题,如何解决?(关键词-performance)
  • ¥15 相敏解调 matlab
  • ¥15 求lingo代码和思路
  • ¥15 公交车和无人机协同运输
  • ¥15 stm32代码移植没反应