Golang中总是遇到死锁问题!为什么?
Golang中总是遇到死锁问题!为什么?
package main
import "fmt"
func main() {
c := make(chan int)
for i := 0; i < 10; i++ {
go func() {
for j := 0; j < 10; j++ {
c <- j
}
}()
}
// 在使用 range 子句时,最后总是会发生死锁。
// close(c) 也不起作用
for range c {
fmt.Println(<-c)
}
// 但这个可以正常工作。为什么??
for k := 0; k < 100; k++ {
fmt.Println(<-c)
}
}
更多关于Golang中总是遇到死锁问题!为什么?的实战教程也可以访问 https://www.itying.com/category-94-b0.html
6 回复
你从未关闭通道。但你需要从正确的位置关闭它。
之前你在哪里尝试过?
更多关于Golang中总是遇到死锁问题!为什么?的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
那么,你的意思是在这种情况下我们不能使用 range 子句吗?
两者:
- 在
for i循环终止之前 - 在
go func()终止之前
for k := 0; k < 100; k++ 循环运行 100 次,并从通道中读取 100 个值——这是可以的。
for range c 循环会无限运行,并消耗通道中的所有值,直到所有 goroutine 都完成。它会继续从空通道中读取,因为剩下的唯一例程会引发死锁。
func main() {
fmt.Println("hello world")
}
当然可以,你只需要一个 sync.WaitGroup 和另一个协程。
死锁的原因是for range c和fmt.Println(<-c)的组合导致了双重接收。for range c会不断从通道接收值,但你在循环体内又执行了一次<-c,这会导致额外的接收操作阻塞。
示例代码分析:
// 问题代码
for range c { // 第一次接收(隐式)
fmt.Println(<-c) // 第二次接收(显式)
}
实际执行流程:
for range c执行隐式接收value := <-cfmt.Println(<-c)执行第二次接收- 发送方只发送了100个值,但这里需要200次接收(100×2)
- 最后10个goroutine在发送第101-200个值时全部阻塞,主goroutine在等待第201次接收时阻塞
解决方案:
// 正确写法1:直接使用range接收的值
for v := range c {
fmt.Println(v)
}
// 正确写法2:显式接收
for {
v, ok := <-c
if !ok {
break
}
fmt.Println(v)
}
// 完整修正示例
package main
import (
"fmt"
"sync"
)
func main() {
c := make(chan int)
var wg sync.WaitGroup
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
for j := 0; j < 10; j++ {
c <- j
}
}()
}
// 关闭通道的goroutine
go func() {
wg.Wait()
close(c)
}()
// 正确接收
for v := range c {
fmt.Println(v)
}
}
第二个for循环能正常工作的原因:
for k := 0; k < 100; k++ { // 明确知道接收100次
fmt.Println(<-c) // 每次循环只接收一次
}
这里没有使用range,明确指定了接收100次,每次循环只执行一次<-c,发送和接收次数匹配(100次发送,100次接收),所以不会死锁。
关键点:
for range channel会一直尝试接收直到通道关闭- 确保发送和接收次数匹配
- 使用
WaitGroup协调goroutine,在发送完成后关闭通道

