【转载】go语言channel与goroutine阻塞的理解

go中的通道就是channel,用于goroutine之间的通讯/数据的传递。可以想象成一个管,一端有人操作发送,一端有人操作接收,如果发送端没有准备好不能发送,即使接收端能正常接收那也不能拿到数据,发送端能正常发送但是接收端不能正常接收,那么整个流程也是不流通的。只有两端都能保证正常发送和接收,数据才能正常流通,你的想法才能实现。

定位:传递消息/数据;使某个goroutine阻塞

以下每个goroutine可以看作一条线,比如main就是main线。

看下面的代码,main函数中只有main一个goroutine(main线程可以看作一个goroutine),由于ch是无缓冲通道,也就是不能暂存数据,只能传递,当main线执行ch <- 1时没有其他goroutine负责接收消息,因此发送者阻塞,main线挂起,此时下面的所有代码将不会执行

var ch = make(chan int)

func main() {
ch <- 1
//若干代码
}

  


所以它会提示:

fatal error: all goroutines are asleep - deadlock!
即没人来接收我要发的数据,我怎么给你发过去?所以我先睡了。

注意,以下两种情况是两码事:

1,发送端正常但接收端无goroutine

2,发送端与接收端goroutine都正常但接收端还没等到数据

此代码中只有一个goroutine, 所以向里面加数据或者存数据,都会锁死通道, 并且阻塞当前 goroutine, 也就是所有的goroutine(其实就main线一个)都在等待通道的开放(没人拿走数据通道是不会开放的),也就是死锁。关于go中通道死锁的具体情况分析请参考这篇文章:go中通道的死锁分析

无缓冲通道  &  有缓冲通道

1,无缓冲的通道在取消息和存消息时都会挂起当前的goroutine,除非管的另一端已准备好

2,有缓冲通道:存消息时如果缓冲区大小大于等于存入的消息数量,那么不会阻塞,反之和无缓冲一样(挂起当前的goroutine)

3,从无缓冲信道取数据,必须要有数据流进来才可以,否则当前线阻塞;数据流入无缓冲信道, 如果没有其他goroutine来拿走这个数据,那么当前线阻塞

看下面的代码,函数loop()被调用两次,两次的goroutine分别是:go关键字开启的goroutine和main线

func loop() {
for i := 0; i < 10; i++ {
fmt.Printf("%d ", i)
}
}

func main() {
go loop()
loop()
}

  



结果是只输出一趟
main线退出的太快,他的loop执行完了但另一个goroutine还没来得及执行。

即要让你的groutine生效,最简单的办法是让main线程延迟退出,阻塞主线:

func main() {
go loop()
loop()
time.Sleep(time.Second)
}

  


go中通道就扮演的是这样的角色!用通道怎么通知main线呢?请看下面的代码:

var complete chan int = make(chan int)

func loop() {
for i := 0; i < 10; i++ {
fmt.Printf("%d ", i)
}

complete <- 0 // 执行完毕了,发个消息,以0作为标志
}


func main() {
go loop()
<- complete // 如果取到消息了说明另一个goroutine跑完了. 否则main在此阻塞住
}

  


main函数的 <- complete 其实就是阻塞住main线不让main线过早跑完,直到main线从complete中读出消息(0)。

关于channel的控制并发实战请移步https://blog.csdn.net/HYZX_9987/article/details/101201408
————————————————
版权声明:本文为CSDN博主「_雨落山岚」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/hyzx_9987/article/details/97528721

posted @ 2020-01-01 12:32  Delo  阅读(597)  评论(0)    收藏  举报