for 循环中的 goroutines 问题

Trouble with goroutines in a for loop

我正尝试在 Exercism 上解决这个问题:

Write a program that counts the frequency of letters in texts using parallel computation.

基本上,我有一个 FreqMap 类型:

type FreqMap map[rune]int

还有一个Frequency函数:

func Frequency(s string) FreqMap {
    m := make(FreqMap)
    for _, v := range s {
        m[v]++
    }
    return m
}

Exercism 提供了一个使用递归实现并发版本的示例,但我想使用 for 循环实现我自己的版本。我提出了以下解决方案,但它不起作用:

func ConcurrentFrequency(l []string) FreqMap {
    c := make(chan FreqMap)
    for i := 0; i < len(l); i++ {
        go func(i int) {
            c <- Frequency(l[i])
        }(i)
    }
    return <- c
}

这似乎 return 仅经过 1 次迭代,c 似乎仅包含 1 个 goroutine 的结果;如果我添加 sync.WaitGroup.

,我会得到相同的结果

你能解释一下我在这里遗漏了什么吗?

提前感谢您的帮助!

您的代码似乎只进行了一次迭代,因为 ConcurrentFrequency return 是通道中的第一个值,仅此而已。我猜你想要这样的东西:

func ConcurrentFrequency(l []string) chan FreqMap {
    c := make(chan FreqMap)
    go func() {
        var wg sync.WaitGroup
        wg.Add(len(l))
        for _, s := range l {
            go func(s string) {
                defer wg.Done()
                c <- Frequency(s)
            }(s)
        }
        wg.Wait()
        close(c)
    }()
    return c
}

现在 return 是地图通道,这些你可能想合并成一张地图:

func main() {
    m := make(FreqMap)
    for v := range ConcurrentFrequency([]string{"foo", "bar","zoo"}) {
        for k, v := range v {
            m[k] += v
        }
    }
    fmt.Println(m)
}

不适合评论的较长解释:

for _, s := range l循环中,所有goroutines都写入同一个通道,但是由于该通道没有缓冲,一旦写入第一个值,它就是"full",这意味着不能写入其他值。所以循环中只有一个goroutine可以完成,wg.Done只被调用一次。因此,如果源数组有多个字符串,其余的 gorutine 将无法完成,直到某些东西开始使用通道中的值。但在您的版本中,它会卡在 wg.Wait 中,因为并非所有 goroutine 都已完成,因此 ConcurrentFrequency 不能 return 到消费者的渠道。 按照我编写 ConcurrentFrequency 的方式,可以将通道 return 发送给消费者,这(从通道读取)使其他 Frequency(s) 调用能够写入通道。