我有一个go-routine将对象添加到通道中,然后我有四个go- routines要处理通道的对象。处理不过是将对象添加到数组而已。但是有时,最终数组中缺少对象。因此,我假设某个时候通道停止收集对象。我有以下代码:
go-routine
go- routines
package main import ( "log" "sync" ) func main() { j := 0 for { if j == 10 { break } wg := sync.WaitGroup{} months := []string{"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul"} hits := make(chan string) i := 0 wg.Add(1) go func() { defer close(hits) for { if i == 25 { wg.Done() return } for _, month := range months { hits <- month } i++ } }() temp := []string{} for updateWorker := 1; updateWorker <= 4; updateWorker++ { wg.Add(1) go func() { for hit := range hits { temp = append(temp, hit) } wg.Done() return }() } wg.Wait() log.Printf("length of temp %+v\n", len(temp)) j++ } }
我正在使用sync库来同步例程。我将同一进程循环10次以测试输出是否一致。我期望这样的输出:
sync
length of temp 175
是175,因为我发送了7个月的字符串25次。但是有时输出少于175,我不知道为什么。我对围棋例程有点初学者。那么有人可以在这里帮助我找到原因吗?谢谢。
问题在于,updateWorkergoroutine都从hits通道收集了结果(到目前为止很好),并且它们都将结果存储在未 同步* 的temp本地变量中。这不行。 *
updateWorker
hits
temp
必须同步访问多个goroutine中的所有变量(其中至少有一个是写操作)。
如果在启用了竞争检测器的情况下运行它,它会发出有关数据竞争的尖叫(go run -race app.go)。
go run -race app.go
如果将updateWorkergoroutine 的数量减少到1,它将立即产生有效的结果,因为这样我们就消除了应用程序的单个数据争用源:
for updateWorker := 1; updateWorker <= 1; updateWorker++ { // ... }
如果要保留多个updateWorkergoroutine,则temp必须同步它们对共享变量的访问。
带有sync.Mutex:
sync.Mutex
var ( mu sync.Mutex temp []string ) for updateWorker := 1; updateWorker <= 4; updateWorker++ { wg.Add(1) go func() { for hit := range hits { mu.Lock() temp = append(temp, hit) mu.Unlock() } wg.Done() return }() }
还要注意,在这个简单的示例中,通过使用多个updateWorkergoroutine并不会获得任何好处,与仅使用其中的一个相比,添加上述同步(锁定)甚至会使性能降低。