fatal error: all goroutines are asleep - deadlock!

一、问题截图

fatal error: all goroutines are asleep - deadlock!

goroutine 1 [chan receive]:
main.main()
	/Users/dianjiu/Codes/go/src/go-learn/demo03/demo.go:36 +0x164

goroutine 18 [chan receive]:
main.consumer(0x140001020c0, 0x14000102060)
	/Users/dianjiu/Codes/go/src/go-learn/demo03/demo.go:10 +0x148
created by main.main
	/Users/dianjiu/Codes/go/src/go-learn/demo03/demo.go:31 +0x104
exit status 2

[Done] exited with code=1 in 10.226 seconds

二、问题代码

package main

import (
	"fmt"
	"strconv"
	"time"
)

func consumer(data chan int, done chan bool) {
	for x := range data {
		// println("recv:", x)
		i := strconv.Itoa(x)
		fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "调用客户端"+i+"结束")
		// <-data
	}
	done <- true
}

func producer(data chan int, done chan bool, i, len int) {
	x := strconv.Itoa(i)
	fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "调用客户端"+x+"开始")
	//单个客户端处理时间为10秒
	time.Sleep(time.Second * 10)
	data <- i
}

func main() {
	fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "开始了")
	done := make(chan bool)
	data := make(chan int)
	go consumer(data, done)
	for i := 0; i < 5; i++ {
		go producer(data, done, i, 5)
	}

	<-done
}

三、问题原因

出现上面协程死锁的原因是生产方法只会在data channel中放五个数据,消费方法在消费完这五个数据后,还会一直等待data channel有新的数据输入,无法结束。这样的话 Go就会判定为死锁。

四、问题解决

为了解决这个问题,我们在生产完五个数据后对data channel进行关闭,这样消费方法就可以正常退出了。

五、具体实现

利用sync.WaitGroup解决,在所有的 data channel 的输入处理之前,wg.Wait()这个goroutine会处于等待状态。当生产方法处理完后(wg.Done),wg.Wait()就会放开执行,执行后面的close(results)。

注意:要把wg.Add(1)放到go producer()外面。如果放到里面的话,(for i := 0; i < 5; i++)里的go producer()有可能会在wg.wait的go func之后执行,这样close(results)就会先执行。

package main

import (
	"fmt"
	"strconv"
	"sync"
	"time"
)

var wg sync.WaitGroup

func consumer(data chan int, done chan bool) {
	for x := range data {
		i := strconv.Itoa(x)
		fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "调用客户端"+i+"结束")
	}
	done <- true
}

func producer(data chan int, done chan bool, i, len int) {
	x := strconv.Itoa(i)
	fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "调用客户端"+x+"开始")
	//单个客户端处理时间为10秒
	time.Sleep(time.Second * 10)
	data <- i
	defer wg.Done()
}

func main() {
	fmt.Println(time.Now().Format("2006-01-02 15:04:05"), "开始了")
	done := make(chan bool)
	data := make(chan int)
	go func() {
		wg.Wait()
		close(data)
	}()
	go consumer(data, done)
	for i := 0; i < 5; i++ {
		wg.Add(1)
		go producer(data, done, i, 5)
	}

	<-done
}

六、问题总结

所有 goroutine 在 main() 函数结束时会一同结束。
在这里插入图片描述

posted @ 2021-03-27 16:48  点九先生  阅读(149)  评论(0编辑  收藏  举报