我正试图在Exercism上解决这个问题:
编写一个程序,使用并行计算计算文本中字母的频率。
基本上,我有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
}
这似乎只在1次迭代后返回,c
似乎只包含1个goroutine的结果;如果我添加sync.WaitGroup
,我会得到相同的结果。
请你解释一下我在这里缺少什么?
提前感谢您的帮助!
答案 0 :(得分:4)
您的代码似乎只进行了一次迭代,因为ConcurrentFrequency
返回通道中的第一个值,就是这样。我想你想要这样的东西:
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
}
现在它返回地图的通道,这些你可能想要组合成单一的地图:
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
循环中,所有goroutine都写入同一个通道,但由于该通道没有缓冲,只要第一个值写入其中,它就是&#34; full&#34 ;,含义没有其他值可写入其中。因此循环中只有一个goroutine可以完成,而wg.Done
只被调用一次。因此,如果源阵列具有多个字符串,则其余的gorutine不能完成,直到某些东西开始消耗来自通道的值。但是在你的版本中它会被卡在wg.Wait
中,因为并非所有goroutine都已完成,因此ConcurrentFrequency
无法将频道返回给消费者。
在我编写ConcurrentFrequency
的方式中,可以将cannel返回给使用者,并且(从通道读取)使其他Frequency(s)
调用可以写入通道。