go routine 不从通道中收集所有对象

go routine not collecting all objects from channel

我有一个 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,不知道为什么。我对 go routines 有点初学者。那么有人可以帮我找到原因吗?谢谢。

问题是 updateWorker goroutines 都从 hits 通道收集结果(到目前为止还不错),并且它们都将结果存储到 temp 局部变量 不同步。这不行。

必须同步访问来自多个 goroutine 的所有变量(其中至少有一个是写入)。

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

如果您将 updateWorker goroutines 的数量减少到 1,它会立即产生有效结果,因为这样我们就消除了您应用程序的单一数据竞争源:

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

如果你想保留多个 updateWorker goroutines,它们对共享 temp 变量的访问必须同步。

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
    }()
}

另请注意,在这个简单的示例中,您使用多个 updateWorker goroutines 没有任何好处,添加上述同步(锁定)甚至会降低与仅使用其中一个 goroutines 相比的性能。

要正确分配工作和收集结果,请查看此答案: