请稍等 ...
×

采纳答案成功!

向帮助你的同学说点啥吧!感谢那些助人为乐的人

chan goroutine 报错的疑惑

//反例,直接报错
func chan0()  {
	c := make(chan int)
	for i := 0; i < 100; i++ {
		c <- i //阻塞了main的goroutine
	}
	go func() {
		for data := range c {
			fmt.Println(data)
		}
	}()
	time.Sleep(time.Millisecond * 10)
}
/**
	必须先启动goroutine接收,才能进行发送,否则报错,
	除非发送也是新启动了一个goroutine.
 */
func chan1() {
	c := make(chan int)
	go func() { //先启动一个新的goroutine接收
		for data := range c {
			fmt.Println(data)
		}
	}()
	//main 中发送,不报错
	for i := 0; i < 100; i++ {
		c <- i
	}
	//上面新的go func... 放到这里就报错.和chan0()函数一样
	//go func() {
	//	for   {
	//		fmt.Println(<-c)
	//	}
	//
	//}()
	time.Sleep(time.Millisecond * 10)
}

/**
	而如果在main中定义一个chan,然后新启动一个发送者的goroutine,那么就不报错,即使没有接收者
 */
func chan2() {
	c := make(chan int)
	go func() { //可以在chan定义的goroutine中发送数据,也可以定义新的goroutine发送数据
		fmt.Println("发送者", time.Now())
		for i := 0; i < 100; i++ {
			fmt.Println("发送数据:", i)
			c <- i
		}
		fmt.Println("关闭chan")
		close(c) //关闭chan,如此,接受者就会感应到,从而接收者会退出接收chan的循环
	}()
	time.Sleep(time.Second * 10)
	//go func() { //接收goroutine中的数据必须在一个新的goroutine中接收数据
	//	fmt.Println("接受者",time.Now())
	//	for data := range c {
	//		fmt.Println(data)
	//	}
	//
	//}()
	time.Sleep(time.Millisecond * 10)
}
/**
	在非main的goroutine中定义的chan,即使没有接收者,即使定以后不再启动一个新的goroutine也不报错
	**老师: 为什么在main中定义的chan,不启动一个新的goroutine就马上报错,而不再main的goroutine中定义,就不报错?**
 */
func chan3() {
	go func() {
		c := make(chan int)
		fmt.Println("发送数据:", 1)
		c <- 1
	}()
	time.Sleep(time.Second * 10)
}
func main() {
	fmt.Println("主程序", time.Now())
	//chan2()
	chan3()
}

正在回答 回答被采纳积分+3

1回答

ccmouse 2019-03-18 21:52:05

同学的这个研究精神值得肯定!

这个报错其实是属于一种尽力而为。它如果确定了就会报错,但是并不能做到所有可能的deadlock都报出来。


0 回复 有任何疑惑可以回复我~
  • 今天学到这里,我也大致研究了一下,我的理解是这样的,其实报错是因为go语言调度程序(这个调度程序是我猜的)检查到了死锁导致main进程进行不下去了,因为我们对通道的操作,无论是发送还是接收都会阻塞,直到有另一个协程对通道执行了对应操作(所以才有了buffer这个东西)。之所以在main里面开另一个协程去发送数据没有报错,我想主要是因为go语言调度程序认为,即便在那个协程里发生了死锁,只要main进程正常退出,这个协程也会跟着一起退出,所以没有报错。同样,在main里开个协程去读某个通道,也是一样会死锁的。
    回复 有任何疑惑可以回复我~ 2019-08-11 15:46:29
问题已解决,确定采纳
还有疑问,暂不采纳
意见反馈 帮助中心 APP下载
官方微信