Golang中如何解决死锁问题
Golang中如何解决死锁问题 不确定为何会出现死锁,尽管通道已关闭,并且我循环了所需的次数。
import (
"fmt"
"time"
)
func sth() {
ticker := time.NewTicker(500 * time.Millisecond)
done := make(chan bool)
c := make(chan int)
go func() {
for i := 0; i < 10; i++ {
select {
case <-done:
return
case <-ticker.C:
c <- i
}
}
close(c)
}()
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
time.Sleep(1 * time.Second)
ticker.Stop()
done <- true
fmt.Println("Ticker stopped")
}
func main() {
sth()
}
这是完整的代码。 这是链接 - https://play.golang.org/p/tQGt2rOQX3U
更多关于Golang中如何解决死锁问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于Golang中如何解决死锁问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
这是一个典型的死锁问题,发生在主goroutine等待从通道c接收数据时。让我们分析一下:
package main
import (
"fmt"
"time"
)
func sth() {
ticker := time.NewTicker(500 * time.Millisecond)
done := make(chan bool)
c := make(chan int)
go func() {
for i := 0; i < 10; i++ {
select {
case <-done:
return
case <-ticker.C:
c <- i // 这里可能阻塞
}
}
close(c)
}()
// 问题在这里:主goroutine等待从c接收10次
for i := 0; i < 10; i++ {
fmt.Println(<-c) // 这里会阻塞等待
}
time.Sleep(1 * time.Second)
ticker.Stop()
done <- true // 这行永远不会执行到,因为上面的循环已经阻塞了
fmt.Println("Ticker stopped")
}
func main() {
sth()
}
死锁原因分析:
- 主goroutine启动了一个工作goroutine
- 工作goroutine使用
select尝试向通道c发送数据 - 主goroutine在
for循环中等待从c接收10次数据 - 但工作goroutine的
ticker每500毫秒才触发一次,发送速度慢于主goroutine的接收速度 - 当主goroutine等待第10个数据时,工作goroutine可能还没有发送第10个数据
- 两个goroutine相互等待,形成死锁
解决方案1:使用缓冲通道
func sth() {
ticker := time.NewTicker(500 * time.Millisecond)
done := make(chan bool)
c := make(chan int, 10) // 添加缓冲区
go func() {
for i := 0; i < 10; i++ {
select {
case <-done:
return
case <-ticker.C:
c <- i
}
}
close(c)
}()
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
ticker.Stop()
done <- true
fmt.Println("Ticker stopped")
}
解决方案2:使用range循环接收
func sth() {
ticker := time.NewTicker(500 * time.Millisecond)
done := make(chan bool)
c := make(chan int)
go func() {
for i := 0; i < 10; i++ {
select {
case <-done:
return
case <-ticker.C:
c <- i
}
}
close(c)
}()
// 使用range自动检测通道关闭
for v := range c {
fmt.Println(v)
}
ticker.Stop()
done <- true
fmt.Println("Ticker stopped")
}
解决方案3:确保发送goroutine先完成
func sth() {
ticker := time.NewTicker(500 * time.Millisecond)
done := make(chan bool)
c := make(chan int)
go func() {
defer close(c)
for i := 0; i < 10; i++ {
select {
case <-done:
return
case <-ticker.C:
c <- i
}
}
}()
// 等待工作goroutine完成
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
ticker.Stop()
done <- true
fmt.Println("Ticker stopped")
}
解决方案4:使用WaitGroup同步
import (
"fmt"
"sync"
"time"
)
func sth() {
ticker := time.NewTicker(500 * time.Millisecond)
done := make(chan bool)
c := make(chan int)
var wg sync.WaitGroup
wg.Add(1)
go func() {
defer wg.Done()
defer close(c)
for i := 0; i < 10; i++ {
select {
case <-done:
return
case <-ticker.C:
c <- i
}
}
}()
for i := 0; i < 10; i++ {
fmt.Println(<-c)
}
wg.Wait()
ticker.Stop()
done <- true
fmt.Println("Ticker stopped")
}
最推荐使用解决方案2,因为它最简洁且能正确处理通道关闭的情况。使用range循环可以确保在通道关闭后自动退出循环,避免死锁。

