2017-10-10 48 views
2

我有一个go-routine将对象添加到通道,然后我有4 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次以测试输出是否一致。我期待这样的输出:

length of temp 175 

它是175,因为我发送7个月字符串25次。 但有时输出小于175,我不知道为什么。我有点习惯性地去做例行公事。那么有人可以帮我找到原因吗?谢谢。

回答

4

的问题是updateWorker够程所有收集来自hits通道(到目前为止好)的结果,他们都将结果存储到temp局部变量不同步。这不好。

必须同步从多个goroutines(其中至少有一个是写入)访问所有变量。

如果您在启用竞争检测器的情况下运行它,它会尖叫数据竞赛(go run -race app.go)。

它立刻得到有效的结果,如果你减少updateWorker够程的数量为1,因为那时我们消除您的应用程序的单一数据争来源:

for updateWorker := 1; updateWorker <= 1; updateWorker++ { 
    // ... 
} 

如果你想保持多个updateWorker够程,其访问共享temp变量必须进行同步。

随着sync.Mutex

temp := []string{} 
mux := sync.Mutex{} 
for updateWorker := 1; updateWorker <= 4; updateWorker++ { 
    wg.Add(1) 
    go func() { 
     for hit := range hits { 
      mux.Lock() 
      temp = append(temp, hit) 
      mux.Unlock() 
     } 
     wg.Done() 
     return 
    }() 
} 

还要注意的是,在这个简单的例子,你通过使用多个updateWorker够程一无所获,将上述同步(锁定)甚至使得它不太高性能相比于具有只是其中之一。

为了正确地分配工作和收集结果,请看这个答案:Is this an idiomatic worker thread pool in Go?

相关问题