跨并行 goroutine 共享通道时的 Golang 竞争条件

Golang race condition when sharing channel across parallel goroutines

我正在编写此示例代码来自学如何跨并行 goroutine 共享通道,但我遇到了竞争条件。该程序应该启动与系统上可用的 CPU 一样多的 goroutine。第一个访问 bl 通道的 goroutine 立即将通道设置为包含 false,这样就没有其他 goroutine 可以访问范围超过 st 通道的循环。当第一个访问 bl 通道的 goroutine 从 st 通道读取并打印每个值时,其他 goroutines 应该结束。

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 { 块中,但这也不起作用。我该如何解决这个问题?

您可以通过通道发送通道以保护所选值的单一访问。只有一个接收者会做这项工作,其他人在收到关闭信号后就会离开。参见 this solution