关于Golang信号channel的长度问题

2018-03-01 10:53:23来源:作者:人点击

分享

首先来看一下一个简单的信号处理的例子

package main
import (
"fmt"
"os"
"os/signal"
)
func main() {
c := make(chan os.Signal, 1) # watch this
signal.Notify(c, os.Interrupt)
s := <-c
fmt.Println("Got signal:", s)
}

首先创建一个容纳信号对象的channel,channel的长度为1。

然后使用Notify注册信号处理,将Interrupt信号和channel关联起来。

进程挂在那里休眠,直到收到一个ctrl+c触发了Interrupt信号,channel里就会被塞进一条信号对象,<-c就会返回,打印信号对象然后退出程序。

上面这个例子是官方文档里提供的一个信号处理的简单例子。

值得注意的是这里的channel是非阻塞型channel,长度为1。那能不能是非阻塞型channel呢,能不能是长度为N的阻塞型channel呢?

我们先看看官方的文档里怎么说的

文档里的relay这个单词很有意思,它的英文是传达、转播,有点像代收的意思。

Notify会使得参数c[channel]来代收输入信号。如果信号参数sig未提供,就会代收所有类型的信号。否则只代收参数里提供的信号类型。

信号代收是非阻塞的,什么意思呢?

如果channel满了,被代收的信号就会被抛弃,因为没有空间容纳了。代收的过程等价于

select {
case c<-sig:
default:
}

如果channel的长度为1,当一连串的信号同时到达,只会有第一个信号会被代收,如果channel消费者没有来得及处理的话。

如果channel的长度为0,信号可能会丢失。比如下面的代码

package main
import (
"fmt"
"os"
"os/signal"
"time"
)
func main() {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
time.Sleep(5 * time.Second)
s := <-c
fmt.Println("Got signal:", s)
}

我们在程序Sleep的时候狂按ctrl+c持续2秒,然后等待到5秒的Sleep结束,你会发现程序依然挂在那里,不会退出,这时需要再按一下ctrl+c才会退出。

阅读更多相关文章,关注知乎专栏【码洞】



微信扫一扫

第七城市微信公众平台