死锁代码示例

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")
}

执行结果