Можно ли захватить сигнал Ctrl+C и запустить функцию очистки, в режиме "отложить"?

Я хочу, чтобы захватить Ctrl+C (SIGINT) сигнал, отправленный с консоли, и распечатайте некоторые частичные итоги выполнения.

это возможно в Golang?

Примечание: когда я впервые опубликовал вопрос, я был смущен Ctrl+C будучи SIGTERM вместо SIGINT.

9 ответов


можно использовать os / сигнал пакет для обработки входящих сигналов. ^C-это РЭР, так что вы можете использовать эту ловушку os.Interrupt.

c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt)
go func(){
    for sig := range c {
        // sig is a ^C, handle it
    }
}()

способ, которым вы заставляете свою программу завершать и печатать информацию, полностью зависит от вас.


это работает:

package main

import (
    "fmt"
    "os"
    "os/signal"
    "syscall"
    "time" // or "runtime"
)

func cleanup() {
    fmt.Println("cleanup")
}

func main() {
    c := make(chan os.Signal)
    signal.Notify(c, os.Interrupt, syscall.SIGTERM)
    go func() {
        <-c
        cleanup()
        os.Exit(1)
    }()

    for {
        fmt.Println("sleeping...")
        time.Sleep(10 * time.Second) // or runtime.Gosched() or similar per @misterbee
    }
}

чтобы добавить немного к другим ответам, Если вы действительно хотите поймать SIGTERM (сигнал по умолчанию, отправленный командой kill), вы можете использовать syscall.SIGTERM вместо ОС.Прерывать. Остерегайтесь, что интерфейс syscall специфичен для системы и может работать не везде (например, в windows). Но это хорошо работает, чтобы поймать как:

c := make(chan os.Signal, 2)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
....

были (на момент публикации) одна или две маленькие опечатки в принятом ответе выше, так что вот очищенная версия. В этом примере я останавливаю профилировщик ЦП при получении Ctrl+C.

// capture ctrl+c and stop CPU profiler                            
c := make(chan os.Signal, 1)                                       
signal.Notify(c, os.Interrupt)                                     
go func() {                                                        
  for sig := range c {                                             
    log.Printf("captured %v, stopping profiler and exiting..", sig)
    pprof.StopCPUProfile()                                         
    os.Exit(1)                                                     
  }                                                                
}()    

все вышеперечисленное, похоже, работает при сращивании, но страница сигналов gobyexample имеет очень чистый и полный пример захвата сигнала. Стоит добавить к этому списку.


это еще одна версия, которая работает, если у вас есть некоторые задачи для очистки. Код оставит clean up process в своем методе.

package main

import (
    "fmt"
    "os"
    "os/signal"
    "syscall"

)



func main() {

    _,done1:=doSomething1()
    _,done2:=doSomething2()

    //do main thread


    println("wait for finish")
    <-done1
    <-done2
    fmt.Print("clean up done, can exit safely")

}

func doSomething1() (error, chan bool) {
    //do something
    done:=make(chan bool)
    c := make(chan os.Signal, 2)
    signal.Notify(c, os.Interrupt, syscall.SIGTERM)
    go func() {
        <-c
        //cleanup of something1
        done<-true
    }()
    return nil,done
}


func doSomething2() (error, chan bool) {
    //do something
    done:=make(chan bool)
    c := make(chan os.Signal, 2)
    signal.Notify(c, os.Interrupt, syscall.SIGTERM)
    go func() {
        <-c
        //cleanup of something2
        done<-true
    }()
    return nil,done
}

в случае, если вам нужно очистить основную функцию, вам нужно захватить сигнал в главном потоке, используя go func ().


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


у вас может быть другой goroutine, который обнаруживает syscall.SIGINT и syscall.Сигналы SIGTERM и ретранслировать их в канал с помощью сигнал.Уведомить. Вы можете отправить крючок этой goroutine с помощью канала и сохранить его в срезе функции. Когда сигнал выключения обнаружен на канале, вы можете выполнить эти функции в срезе. Это можно использовать для очистки ресурсов, ожидания завершения запуска goroutines, сохранения данных или печати итогов частичного запуска.

I написал небольшую и простую утилиту для добавления и запуска крючков при выключении. Надеюсь, это поможет.

https://github.com/ankit-arora/go-utils/blob/master/go-shutdown-hook/shutdown-hook.go

вы можете сделать это "отложить".

пример изящного завершения работы сервера:

srv := &http.Server{}

go_shutdown_hook.ADD(func() {
    log.Println("shutting down server")
    srv.Shutdown(nil)
    log.Println("shutting down server-done")
})

l, err := net.Listen("tcp", ":3090")

log.Println(srv.Serve(l))

go_shutdown_hook.Wait()

просто для записи, если кому-то нужен способ обработки сигналов в Windows. У меня было требование обрабатывать от prog a вызов prog B через os/exec, но prog B никогда не мог закончить изящно, потому что отправка сигналов через ex. УМК.Процесс.Сигнал(системный вызов.SIGTERM) или другие сигналы не поддерживаются в Windows. Способ обработки-создание временного файла в качестве сигнала ex. .сигнал.термин через prog A и prog B должен проверить, существует ли этот файл на базе интервала, если файл существует выйдет из программы и обработает очистку, если это необходимо, я уверен, что есть другие способы, но это сделало работу.