Golang中遇到致命错误:所有goroutine都处于休眠状态——死锁问题!
Golang中遇到致命错误:所有goroutine都处于休眠状态——死锁问题! 我正在尝试让两个 goroutine 对一个整数进行递增。
package main
import (
"fmt"
"sync"
)
var sum = 0
var wg sync.WaitGroup
func main() {
wg.Add(2)
ch := make(chan int)
ch <- sum
go func() {
sum = <-ch
sum++
ch <- sum
defer wg.Done()
}()
go func() {
sum = <-ch
sum++
ch <- sum
defer wg.Done()
}()
wg.Wait()
i := <-ch
close(ch)
fmt.Println(i)
}
出现的错误是:
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/home/peter/work/src/misc2/go-routines-addition/main.go:14 +0x72
exit status 2
>>
fatal error: all goroutines are asleep - deadlock!
goroutine 1 [chan send]:
main.main()
/home/peter/work/src/misc2/go-routines-addition/main.go:14 +0x72
exit status 2
代码可以在这里找到:
https://play.golang.org/p/wyKGpqFWtmG
请问我哪里做错了?
更多关于Golang中遇到致命错误:所有goroutine都处于休眠状态——死锁问题!的实战教程也可以访问 https://www.itying.com/category-94-b0.html
太棒了,谢谢!
更多关于Golang中遇到致命错误:所有goroutine都处于休眠状态——死锁问题!的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
这只是一次为了更好地学习通道而进行的学术性尝试。 感谢您的帮助!
如果你正在学习Go语言,可以跟随这些教程,它们非常棒 https://quii.gitbook.io/learn-go-with-tests/
在尝试了您的解决方案后,我注意到您只调用了 wg.Add(1) 一次。
这里有两个 goroutine,我的理解是应该为每个 goroutine 调用一次 Add。
您能解释一下吗?
是的,你说得对,使用“2”而不是“1”是有道理的,因为有两个 goroutine。如果你使用“2”,就会出现数据竞争,因为代码运行时一个 goroutine 已经结束了。数据竞争条件发生在 goroutine (1) 执行 ch <- sum 之后,控制权交回主线程,此时 wg.Wait() 被执行,但没有 goroutine 在运行。
让我们继续探讨如何解决…
我不太理解在你的代码中通道(channels)的用途。 这里是一种实现方式:
package main
import (
"fmt"
"sync"
"sync/atomic"
)
var sum int64
func main() {
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
atomic.AddInt64(&sum, 1)
}()
go func() {
defer wg.Done()
atomic.AddInt64(&sum, 1)
}()
wg.Wait()
fmt.Println(sum)
}
在你执行 ch <- sum 这一行时,主线程会被阻塞,但此时没有其他可用的 goroutine,因为你是在这行之后才定义并运行它们的。将这行代码移到 wg.Wait() 之前,可能就能解决问题……
我通常会添加一些 println 来追踪程序的执行情况。
package main
import (
"fmt"
"sync"
)
var sum = 0
var wg sync.WaitGroup
func main() {
wg.Add(1)
ch := make(chan int)
go func() {
defer func() {
fmt.Println("(1) defering...")
wg.Done()
}()
fmt.Println("(1) getting channel...")
sum = <-ch
fmt.Println("(1) sum=", sum)
sum++
fmt.Println("(1) Increment=", sum)
ch <- sum
fmt.Println("(1) put in channel...")
}()
go func() {
defer func() {
fmt.Println("(2) defering...")
wg.Done()
}()
fmt.Println("(2) getting channel...")
sum = <-ch
fmt.Println("(2) sum=", sum)
sum++
fmt.Println("(2) Increment=", sum)
ch <- sum
fmt.Println("(2) put in channel...")
}()
fmt.Println("(Main) Sending sum to channel:", sum)
ch <- sum
fmt.Println("(Main) Waiting...")
wg.Wait()
fmt.Println("(Main) Reading from channel...")
i := <-ch
fmt.Println("(Main) Closing channel...")
close(ch)
fmt.Println("(Main) Reading last value...")
fmt.Println(i)
}
你的代码存在两个关键问题导致了死锁:
- 无缓冲通道的同步问题:在第14行
ch <- sum会阻塞主goroutine,直到有另一个goroutine从通道接收数据 - goroutine执行顺序问题:两个goroutine都试图从通道接收数据,但主goroutine已经阻塞在发送操作上
这是修复后的代码:
package main
import (
"fmt"
"sync"
)
var sum = 0
var wg sync.WaitGroup
func main() {
wg.Add(2)
ch := make(chan int)
// 启动goroutine后再发送初始值
go func() {
val := <-ch
val++
ch <- val
wg.Done()
}()
go func() {
val := <-ch
val++
ch <- val
wg.Done()
}()
// 现在发送初始值到通道
ch <- sum
wg.Wait()
i := <-ch
close(ch)
fmt.Println(i) // 输出: 2
}
或者使用带缓冲的通道更简单:
package main
import (
"fmt"
"sync"
)
var sum = 0
var wg sync.WaitGroup
func main() {
wg.Add(2)
// 使用带缓冲的通道
ch := make(chan int, 1)
ch <- sum
go func() {
val := <-ch
val++
ch <- val
wg.Done()
}()
go func() {
val := <-ch
val++
ch <- val
wg.Done()
}()
wg.Wait()
i := <-ch
close(ch)
fmt.Println(i) // 输出: 2
}
关键修改:
- 先启动goroutine再发送数据到无缓冲通道,或者使用带缓冲的通道
- 移除了
defer wg.Done(),直接在操作完成后调用wg.Done() - 使用局部变量
val避免直接操作全局变量sum,减少竞态条件风险
注意:这个示例仍然存在竞态条件,两个goroutine可能以不确定的顺序执行。如果需要确定性的结果,应该使用互斥锁或其他同步机制。

