一尘不染

去例行程序不收集通道中的所有对象

go

我有一个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次以测试输出是否一致。我期望这样的输出:

length of temp 175

是175,因为我发送了7个月的字符串25次。但是有时输出少于175,我不知道为什么。我对围棋例程有点初学者。那么有人可以在这里帮助我找到原因吗?谢谢。


阅读 181

收藏
2020-07-02

共1个答案

一尘不染

问题在于,updateWorkergoroutine都从hits通道收集了结果(到目前为止很好),并且它们都将结果存储在未 同步*
temp本地变量中。这不行。
*

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

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

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

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

如果要保留多个updateWorkergoroutine,则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
    }()
}

还要注意,在这个简单的示例中,通过使用多个updateWorkergoroutine并不会获得任何好处,与仅使用其中的一个相比,添加上述同步(锁定)甚至会使性能降低。

2020-07-02