英文:
I don't understand why this works with an unbuffered channel, or why a wait group is needed
问题
在这段代码中,我调用了一个函数来计算字符串中字母的数量,并返回一个符文的映射。为了利用并发性,我使用goroutines来调用该函数:
func ConcurrentFrequency(l []string) FreqMap {
var wg sync.WaitGroup
wg.Add(len(l))
m := FreqMap{}
// 使用无缓冲通道
// ch := make(chan FreqMap, len(l))
ch := make(chan FreqMap)
for _, s := range l {
go func(s string, ch chan<- FreqMap) {
defer wg.Done()
ch <- Frequency(s)
}(s, ch)
}
go func() {
wg.Wait()
close(ch)
}()
for cm := range ch {
for r, n := range cm {
m[r] += n
}
}
return m
}
如果我尝试在不使用waitgroup和关闭通道的goroutine的情况下运行此代码:
go func() {
wg.Wait()
close(ch)
}()
那么我会遇到死锁问题。
我不明白的是,为什么我能够循环遍历无缓冲通道,并从中读取多个映射。
这是完整的程序:
https://go.dev/play/p/zUwr_HvTT5w
并发方法几乎比顺序方法快不了多少:
goos: linux
goarch: amd64
pkg: letter
cpu: Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz
BenchmarkSequentialFrequency
BenchmarkSequentialFrequency-2 2820 367128 ns/op 17571 B/op 13 allocs/op
BenchmarkConcurrentFrequency
BenchmarkConcurrentFrequency-2 4237 282632 ns/op 12682 B/op 72 allocs/op
PASS
ok letter 3.320s
英文:
In this code, I call a function which counts the number of letters in a string, and return a map of runes. To leverage concurrency, I call the function using goroutines:
func ConcurrentFrequency(l []string) FreqMap {
var wg sync.WaitGroup
wg.Add(len(l))
m := FreqMap{}
// Using unbuffered channel
// ch := make(chan FreqMap, len(l))
ch := make(chan FreqMap)
for _, s := range l {
go func(s string, ch chan<- FreqMap) {
defer wg.Done()
ch <- Frequency(s)
}(s, ch)
}
go func() {
wg.Wait()
close(ch)
}()
for cm := range ch {
for r, n := range cm {
m[r] += n
}
}
return m
}
If I try this code without using a waitgroup and the goroutine which closes the channel:
go func() {
wg.Wait()
close(ch)
}()
, then I get a deadlock.
What I don't understand, is why I am able to loop over the unbuffered channel, and read multiple maps from it.
This is the full program:
https://go.dev/play/p/zUwr_HvTT5w
And the concurrent method is barely faster than the sequential method:
goos: linux
goarch: amd64
pkg: letter
cpu: Intel(R) Core(TM) i5-6200U CPU @ 2.30GHz
BenchmarkSequentialFrequency
BenchmarkSequentialFrequency-2 2820 367128 ns/op 17571 B/op 13 allocs/op
BenchmarkConcurrentFrequency
BenchmarkConcurrentFrequency-2 4237 282632 ns/op 12682 B/op 72 allocs/op
PASS
ok letter 3.320s
答案1
得分: 2
如果你移除最终关闭通道的goroutine,for循环将永远无法终止。一旦所有发送值的goroutine完成,只剩下一个goroutine,并且它会永远被阻塞,等待通道关闭。
缓冲通道与此问题无关。它们只有在发送者被阻塞时才有帮助,但这里的问题是接收者被阻塞。
英文:
A for-range loop over a channel continues until the channel is closed.
If you remove the goroutine that eventually closes the channel, the for loop can never terminate. Once all goroutines sending values are done there is only one goroutine remaining and it is blocked forever, waiting for the channel to be closed.
Buffered channels have nothing to do with this problem. They only help with blocked senders, but here the problem is a blocked receiver.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论