我正在编写此示例代码来教自己如何在并行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尚未插入
您可以通过通道发送通道,以保护所选值的单次访问。 只有接收器可以完成工作,其他接收器在收到关闭信号后会离开。 查看此解决方案
- 当前解决方案的问题在于,通过b1通道发送的最后一个goroutine将等待接收器获取发送的值,因此goroutine将永远不会退出,也不会等待整个goroutine组的主goroutine。 当通道的缓冲区大小为1时,可以使用缓冲的通道(b1:= make(chan bool,1))进行修复。 这将使您的方法可行,但我认为我提出的解决方案更明确地说明了为何通过渠道发送价值
- 太棒了! 谢谢你的例子。 确实有帮助。