我从下面的goroutine发送频道时遇到以下代码陷入死锁:
package main
import (
"fmt"
"sync"
)
func main() {
for a := range getCh(10) {
fmt.Println("Got:", a)
}
}
func getCh(n int) <-chan int {
var wg sync.WaitGroup
ch := make(chan int)
defer func() {
fmt.Println("closing")
wg.Wait()
close(ch)
}()
wg.Add(1)
go func() {
defer wg.Done()
for i := 0; i < n; i++ {
ch <- i
}
}()
wg.Add(1)
go func() {
defer wg.Done()
for i := n; i < 0; i-- {
ch <- i
}
}()
return ch
}
我知道在wg.Wait()
中使用defer
是合法的。但我还没有找到一个使用频道作为返回值的函数。
答案 0 :(得分:5)
我认为您犯的错误是您认为deferred
函数也会异步运行。但事实并非如此,因此getCh()
将在其延迟部分中阻塞,等待WaitGroup。但由于没有人从通道读取,因此写入它的goroutine无法返回,因此WaitGroup会导致死锁。尝试这样的事情:
func getCh(n int) <-chan int {
ch := make(chan int)
go func() {
var wg sync.WaitGroup
wg.Add(1)
go func(n int) {
defer wg.Done()
for i := 0; i < n; i++ {
ch <- i
}
}(n)
wg.Add(1)
go func(n int) {
defer wg.Done()
for i := n; i > 0; i-- {
ch <- i
}
}(n)
wg.Wait()
fmt.Println("closing")
close(ch)
}()
return ch
}
答案 1 :(得分:1)
由于您没有使用任何缓冲频道,因此您的频道似乎正在阻止。看看这个简单的例子https://play.golang.org/p/zMnfA33qZk
<
请记住,频道在填充时会阻止。我不确定你的代码的目标是什么,但看起来你的目标是使用缓冲通道。从有效的go https://golang.org/doc/effective_go.html#channels
开始,这是一个很好的结果接收器始终阻塞,直到有数据要接收为止。如果通道未缓冲,则发送方将阻塞,直到接收方收到该值。如果通道有缓冲区,则发送方仅阻塞,直到将值复制到缓冲区为止;如果缓冲区已满,则表示等待某个接收方检索到一个值。