前言:
如果判断golang的channel是否关闭,我想玩go的同学都知道,data, ok := <- chan,当ok不是true的时候,说明是channel关闭了。 那么问题来了,channel关闭了,我们是否可以立马获取到channel被关闭的状态?我想这个问题不少人没有去想吧?
为什么有这样的问题? 来自我的一个bug,我期初认为close了一个channel,消费端的goroutine自然是可以拿到channel的关闭状态。然而事实并不是这样的。 只有当channel无数据,且channel被close了,才会返回ok=false。 所以,只要有堆积,就不会返回关闭状态。导致我的服务花时间来消费堆积,才会退出。
测试channel的关闭状态
我们发现已经把channel关闭了,只要有堆积的数据,那么ok就不为false,不为关闭的状态。
go runtime channel源码分析
首先我们来分析下go runtime/chan.go的相关源码,记得先前写过一篇golang channel实现的源码分析,有兴趣的朋友可以翻翻。 这次翻channel源码主要探究下close chan过程及怎么查看channel是否关闭?
下面是channel的hchan主数据结构,closed字段就是标明是否退出的标识。
下面是关闭channel的函数,修改了closed字段为1, 1为退出。
下面是channel的recv消费者方法,也就是 data, ok := <- chan。if c.closed != 0 && c.qcount == 0 只有当 closed为1 并且 堆积为0的时候,才会返回false。 一句话,channel已经closed,并且没有堆积任务,才会返回关闭channel的状态。
channel代码里没有找到一个查询channel关闭的方法。
解决方法
那么如何在channel堆积的情况下,得知channel已经关闭了 ?
第一种方法:
可以直接读取channel结构hchan的closed字段,但问题chan.go没有开放这样的api,所以我们要用reflect这个黑科技了。 (不推荐大家用reflect的方法,因为看起来太黑科技了)
第二种方法:
配合一个context或者一个变量来做。就拿context来说,那么select不仅可以读取数据chan,且同事监听<- context.Done() , 当context.Done()有事件,直接退出就ok了。
总结:
这个问题肯定不是channel的问题了,只能说我对channel理解还是不够,还是要继续钻研golang runtime源码。没了,希望这篇文章对大家有用。