我正在编写此示例代码来教自己如何在并行goroutine中共享频道,并且遇到了竞争问题。该程序应启动与系统上可用CPU一样多的goroutine。第一个访问bl通道的goroutine立即将通道设置为包含false,以便其他goroutine无法访问范围在st通道上的循环。其他goroutine应该以访问bl通道的第一个goroutine从st通道读取并输出每个值的方式结束。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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)
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
$ 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 {块中,但这也不起作用。我该如何解决?


您可以通过通道发送通道,以保护所选值的单次访问。 只有接收器可以完成工作,其他接收器在收到关闭信号后会离开。 查看此解决方案

  • 当前解决方案的问题在于,通过b1通道发送的最后一个goroutine将等待接收器获取发送的值,因此goroutine将永远不会退出,也不会等待整个goroutine组的主goroutine。 当通道的缓冲区大小为1时,可以使用缓冲的通道(b1:= make(chan bool,1))进行修复。 这将使您的方法可行,但我认为我提出的解决方案更明确地说明了为何通过渠道发送价值
  • 太棒了! 谢谢你的例子。 确实有帮助。