在并行goroutine之间共享通道时的Golang竞争条件

时间:2017-01-20 04:21:42

标签: go parallel-processing race-condition goroutine

我正在编写这个示例代码来教我自己如何在并行goroutine之间共享通道,而且我遇到了竞争条件。该程序应该启动尽可能多的goroutine,因为系统上有可用的CPU。访问bl通道的第一个goroutine会立即将通道设置为false,以便其他goroutine不能访问st通道上的循环。其他goroutine应该作为访问bl通道的第一个goroutine从st通道读取并打印每个值。

package main

import (
    "fmt"
    "runtime"
    "strconv"
    "math/rand"
    "time"
    "sync"
)

func main () {
    runtime.GOMAXPROCS(runtime.NumCPU())
    var wg sync.WaitGroup
    st := make(chan string)
    bl := make(chan bool)
    go func() {bl <- true}()

    for i := 0; i < runtime.NumCPU(); i++ {
        wg.Add(1)
        go func(x int) {
            defer wg.Done()
            fmt.Println("me: ", strconv.Itoa(x))

            can := <- bl
            bl <- false

            if can {
                for val := range st {
                    t      := strconv.Itoa(rand.Int()%3)+"s"
                    dur, _ := time.ParseDuration(t)
                    time.Sleep(dur)

                        fmt.Println("time: ", t," i: ", strconv.Itoa(x), val)
                }
            }
            fmt.Println("done: ", strconv.Itoa(x))
        }(i)
    }

    for i := 0; i < 10; i++ {
        st <- "n: "+strconv.Itoa(i)
    }

    wg.Wait()
    close(st)
}

当我运行代码时,出现以下错误:

$ go run share.go 
me:  1
me:  0
me:  2
me:  3
done:  0
done:  2
time:  2s  i:  1 n: 0
time:  1s  i:  1 n: 1
time:  0s  i:  1 n: 2
time:  2s  i:  1 n: 3
time:  2s  i:  1 n: 4
time:  2s  i:  1 n: 5
time:  0s  i:  1 n: 6
time:  2s  i:  1 n: 7
time:  2s  i:  1 n: 8
time:  2s  i:  1 n: 9
fatal error: all goroutines are asleep - deadlock!

goroutine 1 [semacquire]:
sync.runtime_Semacquire(0xc4200701bc)
    /usr/local/go/src/runtime/sema.go:47 +0x30
sync.(*WaitGroup).Wait(0xc4200701b0)
    /usr/local/go/src/sync/waitgroup.go:131 +0x97
main.main()
    /share.go:80 +0x1ef

goroutine 36 [chan receive]:
main.main.func2(0xc4200701b0, 0xc42006e0c0, 0xc42006e060, 0x1)
    /share.go:64 +0x23e
created by main.main
    /share.go:73 +0x127

goroutine 38 [chan send]:
main.main.func2(0xc4200701b0, 0xc42006e0c0, 0xc42006e060, 0x3)
    /share.go:61 +0x1ef
created by main.main
    /share.go:73 +0x127
exit status 2

我不确定如何处理bl通道的竞争条件。似乎最后一个goroutine试图从bl通道读取但是它没有任何东西可供阅读,因为它之前的goroutine还没有插入false。不确定我的直觉是否正确。我在代码周围放置了打印线,这似乎至少发生了什么。我也尝试在bl通道周围放置一个if,但结果却出现了同样的问题。我还在bl <- false块中移动if can {,但这也无效。我该如何解决这个问题?

1 个答案:

答案 0 :(得分:1)

您可以通过频道发送频道,以保护所选值的单一访问。只有接收器才能完成工作,其他接收器将在收到关闭信号时离开。见this solution