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)
调用能够写入通道。
我正尝试在 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)
调用能够写入通道。