乐趣区

关于go:为什么-signalNotify-使用缓冲通道

在本文中,咱们将向您介绍为什么 signal.Notify 要应用缓冲通道。当咱们想做优雅的敞开时,咱们会应用这个性能来失常敞开服务或连贯。通过signal,咱们能够检测到信号的起源,并进行后续工作(敞开 DB 连贯,查看工作是否实现……等)。

package main

import (
    "fmt"
    "os"
    "os/signal"
)

func main() {
    // Set up channel on which to send signal notifications.
    // We must use a buffered channel or risk missing the signal
    // if we're not ready to receive when the signal is sent.
    c := make(chan os.Signal, 1)
    signal.Notify(c, os.Interrupt)

    // Block until a signal is received.
    s := <-c
    fmt.Println("Got signal:", s)

下面的示例分明地表明,如果您不应用缓冲通道,则存在肯定的无奈捕捉信号的危险。那么为什么会有这样的形容呢?让咱们看看其余例子。

应用无缓冲通道

将代码更改为以下内容:

package main

import (
    "fmt"
    "os"
    "os/signal"
)

func main() {c := make(chan os.Signal)
    signal.Notify(c, os.Interrupt)

    // Block until a signal is received.
    s := <-c
    fmt.Println("Got signal:", s)
}

运行下面的代码,按 ctrl + c,你会看到Got signal: interrupt,那么如果在承受channle 之前咱们还有一些很简单的工作要做会发什么,先 time.Sleep 用来测试一下。

package main

import (
    "fmt"
    "os"
    "os/signal"
)

func main() {c := make(chan os.Signal)
    signal.Notify(c, os.Interrupt)

    time.Sleep(5 * time.Second)

    // Block until a signal is received.
    s := <-c
    fmt.Println("Got signal:", s)
}

你会发现,在这五秒钟内,无论你怎么按 ctrl + c,程序都不会进行,五秒钟后,程序也不会进行。您须要再次按 ctrl + c,而后程序将进行。咱们冀望的是,如果你在前五秒的任何工夫按 ctrl + c,实践上你会在五秒后失常收到第一个信号。让咱们看看为什么。

造成起因

咱们关上 Golang 的 singal.go 文件,找到 process 函数,能够看到局部代码:

for c, h := range handlers.m {if h.want(n) {
        // send but do not block for it
        select {
        case c <- sig:
        default:
        }
    }
}

在下面的代码中能够看到,如果应用无缓冲通道,5 秒内收到的任何信号都会运行到默认状态,所以通道不会收到任何值,这就是为什么 5 秒内的任何动作都不会收到的起因。为了防止这种状况,咱们通常将信号通道设置为缓冲区 1,以防止打断程序的执行,以确保主程序能够接管到信号。

退出移动版