死锁代码示例
package main
import (
"fmt"
"sync"
"time"
)
func sendNumbers(ch chan int, data chan MyData, wg *sync.WaitGroup) {
defer wg.Done() // 确保goroutine完成时调用Done
for i := 1; i <= 2; i++ {
ch <- i // 向channel发送数据
time.Sleep(1 * time.Second)
}
fmt.Println("结束 ch 通道的发送")
fmt.Println("开始 data 通道的发送")
data <- MyData{name: "ryan", age: 28}
close(data) // 关闭data channel
close(ch) // 关闭channel
fmt.Println("结束 data 通道的发送")
}
func getNumbers(ch chan int, data chan MyData, wg *sync.WaitGroup) {
defer wg.Done() // 确保goroutine完成时调用Done
fmt.Println("start get Numbers")
for num := range ch { // 从channel接收数据,直到channel被关闭
fmt.Printf("%d ", num)
}
fmt.Println("end get Numbers")
fmt.Println("start get data")
person := <-data
fmt.Printf("Received person: %+v\n", person)
}
type MyData struct {
name string
age int
}
func main() {
var wg sync.WaitGroup
ch := make(chan int) // 创建一个int类型的channel
data := make(chan MyData) // 创建一个MyData类型的channel
wg.Add(2) // 设置等待两个goroutine
go sendNumbers(ch, data, &wg) // 启动一个新的goroutine来发送数据到channel
go getNumbers(ch, data, &wg) // 启动一个新的goroutine来从channel接收数据
wg.Wait() // 等待所有的goroutine完成
fmt.Println("All workers done")
}
上面这段代码执行结果为
输出报错,出现死锁。
原因分析
为了方便分析原因,我在上面的代码中打印了日志。从日志可以看出在“开始 data 通道发送”后,就中断了。可以看到后面的代码是发送MyData 结构体到 data 通道,从这里开始中断了,出现死锁,看 getNumbers 函数中循环获取 ch 通道的数据,知道通道关闭才会结束循环,但是 sendNumbers 函数在还没有关闭通道 ch 的时候,就开始发送数据到 data 通道,也就是 getNumbers 函数的 data 通道永远获取不到数据,因为 ch 一直在循环等待通道的数据,堵塞了,不会继续向下执行。
修复
- ch 通道数据发送完成后,就得及时关闭
- 新起一个 goroutine 用来获取data 通道的数据(下面的代码就是根据这种方式修改的)
package main
import (
"fmt"
"sync"
"time"
)
func sendNumbers(ch chan int, data chan MyData, wg *sync.WaitGroup) {
defer wg.Done() // 确保goroutine完成时调用Done
for i := 1; i <= 2; i++ {
ch <- i // 向channel发送数据
time.Sleep(1 * time.Second)
}
fmt.Println("结束 ch 通道的发送")
fmt.Println("开始 data 通道的发送")
data <- MyData{name: "ryan", age: 28}
close(data) // 关闭data channel
close(ch) // 关闭channel
fmt.Println("结束 data 通道的发送")
}
func getNumbers(ch chan int, data chan MyData, wg *sync.WaitGroup) {
defer wg.Done() // 确保goroutine完成时调用Done
fmt.Println("start get Numbers")
for num := range ch { // 从channel接收数据,直到channel被关闭
fmt.Printf("%d ", num)
}
fmt.Println("end get Numbers")
}
func getData(data chan MyData, wg *sync.WaitGroup) {
defer wg.Done() // 确保goroutine完成时调用Done
fmt.Println("start get data")
person := <-data
fmt.Printf("Received person: %+v\n", person)
}
type MyData struct {
name string
age int
}
func main() {
var wg sync.WaitGroup
ch := make(chan int) // 创建一个int类型的channel
data := make(chan MyData) // 创建一个MyData类型的channel
wg.Add(3) // 设置等待两个goroutine
go sendNumbers(ch, data, &wg) // 启动一个新的goroutine来发送数据到channel
go getNumbers(ch, data, &wg) // 启动一个新的goroutine来从channel接收数据
go getData(data, &wg) // 启动一个新的goroutine来从channel接收数据
wg.Wait() // 等待所有的goroutine完成
fmt.Println("All workers done")
}
执行结果