原创分享 為什麼 signal.Notify 要使用 buffered channel

appleboy · 2021年03月30日 · 38 次阅读

原文來自: 『為什麼 signal.Notify 要使用 buffered channel

如果不了解什麼是 buffer 或 unbuffer channel 的朋友們,可以參考這篇文章先做初步了解,本文要跟大家介紹為什麼 signal.Notify 要使用 buffered channel 才可以,底下先來看看如何使用 signal.Notify,當我們要做 graceful shutdown 都會使用到這功能,想要正常關閉服務或連線,透過 signal 可以偵測訊號來源,執行後續相關工作 (關閉 DB 連線,檢查 Job 是否結束 ... 等)。

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)
}

上面例子可以很清楚看到說明,假如沒有使用 buffered channel 的話,你有一定的風險會沒抓到 Signal。那為什麼會有這段說明呢?底下用其他例子來看看。

使用 unbuffered channel

將程式碼改成如下:

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 func,可以看到部分程式碼

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

可以看到上述程式碼,如果使用 unbuffered channel,那麼在五秒內接收到的任何訊號,都會跑到 default 條件內,所以造成 Channel 不會收到任何值,這也就是為什麼五秒內的任何動作,在五秒後都完全收不到。為了避免這件事情,所以通常我們會將 signal channel 設定為 buffer 1,來避免需要中斷程式時,確保主程式可以收到一個 signal 訊號。

更多原创文章干货分享,请关注公众号
  • 加微信实战群请加微信(注明:实战群):gocnio
暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册