Go на примерах: Сигналы

Иногда нам нужно, чтобы Go-программы грамотно обрабатывали Unix-сигналы. Например, мы можем захотеть, чтобы сервер корректно завершал работу при получении SIGTERM, или чтобы инструмент командной строки прекращал обработку ввода при получении SIGINT. Рассмотрим, как обрабатывать сигналы в Go с помощью каналов.

package main
import (
    "fmt"
    "os"
    "os/signal"
    "syscall"
)
func main() {

Уведомление о сигналах в Go работает через отправку значений os.Signal в канал. Создадим канал для получения этих уведомлений. Обратите внимание, что этот канал должен быть буферизованным.

    sigs := make(chan os.Signal, 1)

signal.Notify регистрирует указанный канал для получения уведомлений о заданных сигналах.

    signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)

Мы могли бы получать сигналы из sigs прямо здесь, в функции main, но давайте посмотрим, как это можно сделать в отдельной горутине — для демонстрации более реалистичного сценария корректного завершения.

    done := make(chan bool, 1)

Эта горутина выполняет блокирующее чтение из канала сигналов. Когда сигнал получен, она выводит его и уведомляет программу, что можно завершаться.

    go func() {
        sig := <-sigs
        fmt.Println()
        fmt.Println(sig)
        done <- true
    }()

Программа будет ждать здесь, пока не получит ожидаемый сигнал (на что указывает отправка значения в done горутиной выше), а затем завершится.

    fmt.Println("awaiting signal")
    <-done
    fmt.Println("exiting")
}

При запуске программа заблокируется в ожидании сигнала. Нажав ctrl-C (что терминал отображает как ^C), мы отправим сигнал SIGINT, и программа выведет interrupt, а затем завершится.

$ go run signals.go
awaiting signal
^C
interrupt
exiting

Далее: .