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



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



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



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

534   9  

9 ответов:

можно использовать os / signal пакет для обработки входящих сигналов. ^C-это SIGINT, так что вы можете использовать эту ловушку 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)
....

были (во время публикации) одна или две маленькие опечатки в принятом ответе выше, так что вот очищенная версия. В этом примере я останавливаю профилировщик CPU при получении 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 имеет очень чистый и полный пример захвата сигнала. Стоит добавить к этому списку.

Это еще одна версия, которая работает в случае, если у вас есть какие-то задачи по очистке. Код оставит процесс очистки в их методе.

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 должен проверить, существует ли этот файл на интервальной базе, если файл существует выйдете из программы и обрабатывать очистку, если это необходимо, я уверен, что есть и другие способы, но это сделало работу.

Comments

    Ничего не найдено.