最近写代码 gofmt报错了,引起报错的语句如下:
c := make(chan os.Signal)
signal.Notify(c, syscall.SIGTERM)
<-c
用gofmt检查代码的时候发现报错:
the channel used with signal.Notify should be buffered
于是点开看了一下signal.Notify函数里面到底是咋回事
先看到的是注释已经写清楚了,让channel用有buffer的通道:
如果不用有buffer的通道会咋样呢?
先说一下结论:
如果使用无buffer通道,可能会造成信号丢失。
换句话说,如果信号的发送者只发送一次信号的话,第三行代码可能会因为信号丢失而永远阻塞:
c := make(chan os.Signal)
signal.Notify(c, syscall.SIGTERM)
<-c
当然我就蒙b了,我都没有从通道读数据呢?数据怎么可能就没了?
看了一下signal.Notify的源代码 主要看下面这一点就够了
最终信号会被传入process这个函数里,然后执行截图中的select语句。
select 语句配合channel的效果就是,随机执行非阻塞的case,这里由于可能没有人读c这个chan,因此case c<-sig就是阻塞的,所以这个case是不执行的 直接执行default,然后执行下一个循环了,所以sig就被丢掉了。
所以 当信号的发起者发送的进程所建立的chan是一个无buffer chan,很有可能这个信号就丢掉了。丢掉的地方不是在传入chan后丢掉的,而是这个signal直接就没有往c里面传,就扔掉了,当然会出现信号丢失的问题。
这里还有一个疑问需要有空的时候想一下:既然存在这样的问题,go为什么要这样设计呢???