Golang通道不理解之处

我的golang版本是1.14

就是学习通道的时候看到一段代码,我运行了一下,发现结果有两种:

package main

import "fmt"

func main() {
    chanInt := make(chan int)
    defer close(chanInt)
    go func() {
        for {
            res, ok := <-chanInt
            if !ok {
                break
            }
            fmt.Println(res, ok)
        }
    }()
    chanInt <- 1
    chanInt <- 10
}

代码很简单,新开一个goroutine通过for循环读取通道的数据。
然后我执行的时候发现有两种结果:

而且第二种结果的概率要小一点。

还有我试着把代码的无缓冲改为有缓冲的情况,chanInt := make(chan int, 2)其他不变,发现运行之后什么打印结果也没有,空空如也。

思索许久还是不明白代码的情况,希望大伙帮忙解答一下两个问题:

  1. 为什么无缓冲的时候,打印结果会有两种情况?【我的理解是:第二种情况应该是:chanInt <- 10之后,携程就读取了res, ok := <-chanInt,还没到打印,主协程就结束了】
  2. 当有缓冲通道的时候为什么打印不出任何东西?
阅读 1.7k
2 个回答

这个其实挺简单的,这里一方面就是你说的,还没打印,主协程就结束了,这个可以说是前情提要了,再有一个概念就是协程启动也需要消耗时间的,然后就说到了关于两种通道了:阻塞通道缓冲通道

  1. 先来看阻塞通道的情况:
    阻塞嘛,那么你发送数据,只要没被接收,那么肯定是阻塞的,这里发送了两次chanInt <- 1chanInt <- 10,那么,你启动的协程肯定要接收完chanInt <- 1才能继续发送chanInt <- 10,那么这个接收协程肯定会接收到一次的,也就是主协程肯定要等你这个接收协程执行一次。然后呢,这个执行期间,每次阻塞的等待时间肯定是不一样的,毕竟一台电脑又不可能只执行你这个程序,会受到其他干扰因素影响的,那么如果chanInt <- 1发送完后,这里等待接收的协程执行的时间如果稍微长一点,那么第二个接收协程就启动成功了,然后再继续发送chanInt <- 10的时候,也可以显示出来,从你这表现看,大部分时候都能把这两个显示出来,说明这种阻塞情况时间长一些的情况占据更多。
  2. 再来看缓冲通道情况:
    这个缓冲通道,那么来说,根本不存在阻塞,我主协程根本不管你子协程启动没启动,反正有缓存,我直接把数据赛道缓冲通道,然后再关闭通道就不管你了,我主协程任务就完成了,这个子协程根本都没办法启动起来,他那还没开始呢,连准备都没有你就结束了。这里就是协程启动消耗的时间比主协程执行时间要更长 导致的,那么对于缓冲通道的情况下,如果你不做个定时器time.Sleep(time.Duration(1))阻塞一下主协程的话,那么来说,如果,我是说如果啊,有种可能,就是这种有缓冲的情况下如果某些情况导致主协程结束得比子协程慢,也是有可能会产生第一种情况的显示结果的。

后续:
为了验证,可以试一下以阻塞通道2个容量缓冲通道为例,然后验证一下发送>=3次可见,

  1. 第一种情况,显示的结果量为发送的次数或发送的次数-1
  2. 第二种情况,显示的结果量为发送的次数减去缓冲容量至发送的次数这个区间

1.无缓冲通道读写都阻塞程序运行,当收发双方都准备好才可以,代码chanInt<-1执行后,被子goroutine取走,chanInt<-10执行后,可能触发子goroutine执行不及时,主goroutine退出,子goroutine自动退出
2.修改为有缓冲通道后,在通道未满之前,读写都不阻塞,所以主goroutine退出,子goroutine还没来得及执行也退出了

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题
宣传栏