我正在编写这个示例代码来教我自己如何在并行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 {
,但这也无效。我该如何解决这个问题?