Можно ли захватить сигнал Ctrl + C и запустить функцию очистки «отложенным» способом?

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

Возможно ли это на Голанге?

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

Ответы на вопрос(9)

Просто для записи, если кому-то нужен способ обработки сигналов в Windows. У меня было требование обрабатывать из prog A вызывающий prog B через os / exec, но prog B никогда не смог завершиться изящно, потому что отправлял сигналы через ex. cmd.Process.Signal (syscall.SIGTERM) или другие сигналы не поддерживаются в Windows. Я справился с этим, создав временный файл в качестве сигнала ex. .signal.term через prog A и prog B необходимо проверить, существует ли этот файл на интервальной основе, если файл существует, он выйдет из программы и при необходимости выполнит очистку, я уверен, что есть и другие способы, но это помогло.

В принятом ответе (на момент публикации) была одна или две маленькие опечатки, поэтому здесь приведена исправленная версия. В этом примере я останавливаю профилировщик ЦП при получении 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)                                                     
  }                                                                
}()    
 04 авг. 2013 г., 17:54
Обратите внимание, что для того, чтобы программа могла получить процессорное время для обработки сигнала, основная программа должна вызвать операцию блокировки или вызовruntime.Gosched в соответствующем месте (в главном цикле вашей программы, если он есть)

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

 05 дек. 2016 г., 05:56
это такой удивительно лаконичный пример

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

 14 окт. 2016 г., 08:54
Столько строк кода и зависимость от внешней библиотеки, что можно сделать в четырех строках кода? (согласно принятому ответу)

Чтобы немного добавить к другим ответам, если вы действительно хотите перехватить SIGTERM (сигнал по умолчанию, посылаемый командой kill), вы можете использоватьsyscall.SIGTERM вместо os.Interrupt. Помните, что интерфейс системного вызова зависит от системы и может работать не везде (например, в Windows). Но это хорошо работает, чтобы поймать оба:

c := make(chan os.Signal, 2)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
....
 06 апр. 2017 г., 18:24
Как насчет os.Kill?
 07 апр. 2017 г., 23:44
@adamlamar Ааа, это имеет смысл. Спасибо!
 25 авг. 2013 г., 15:31
signal.Notify Функция позволяет указывать несколько сигналов одновременно. Таким образом, вы можете упростить свой код доsignal.Notify(c, os.Interrupt, syscall.SIGTERM).
 25 авг. 2013 г., 21:55
Я думаю, что узнал об этом после публикации. Исправлена!
 07 апр. 2017 г., 23:14
@ Затмение Отличный вопрос!os.Kill corresponds вsyscall.Kill, который является сигналом, который может быть отправлен, но не перехвачен. Это эквивалентно командеkill -9 <pid>, Если вы хотите пойматьkill <pid> и изящно выключить, вы должны использоватьsyscall.SIGTERM.
Решение Вопроса

Вы можете использоватьOS / сигнал пакет для обработки входящих сигналов. ^ 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
    }
}()

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

 04 авг. 2013 г., 17:53
Обратите внимание, что для того, чтобы программа могла получить процессорное время для обработки сигнала, основная программа должна вызвать операцию блокировки или вызовruntime.Gosched в соответствующем месте (в главном цикле вашей программы, если он есть)
 30 июн. 2012 г., 23:31
@dystroy: Конечно, если вы действительно собираетесь прекратить программу в ответ на первый сигнал. Используя цикл, вы можете поймать все сигналы, если вы решилиnot прекратить программу.
 Sebastián Grignoli29 июн. 2012 г., 23:51
Спасибо! Так что ... ^ Это не SIGTERM, тогда? ОБНОВЛЕНИЕ: Извините, ссылка, которую вы предоставили, достаточно подробная!
 30 июн. 2012 г., 09:34
Вместоfor sig := range g {Вы также можете использовать<-sigchan как в этом предыдущем ответе:stackoverflow.com/questions/8403862/…
 17 нояб. 2012 г., 23:33
Примечание: вы должны действительно собрать программу, чтобы это работало. Если вы запустите программу черезgo run в консоли и отправьте SIGTERM через ^ C, сигнал записывается в канал, и программа отвечает, но, кажется, неожиданно выпадает из цикла. Это потому, что СИГРЕРМgo run также! (Это вызвало у меня существенную путаницу!)

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

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

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

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

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 ().

Это работает:

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
    }
}
 06 апр. 2017 г., 18:23
Почему вы используете неблокирующий канал? Это необходимо?
 05 мая 2017 г., 07:34
@ Барри, почему размер буфера 2 вместо 1?
 09 февр. 2018 г., 22:49
Вот выдержка изdocumentation, & quot; Пакетный сигнал не будет блокировать отправку на c: вызывающий должен убедиться, что c имеет достаточно буферного пространства, чтобы не отставать от ожидаемой скорости сигнала. Для канала, используемого для уведомления только об одном значении сигнала, достаточно буфера размера 1. & quot;
 06 окт. 2014 г., 12:34
+1 за использование каналов & apos; синтаксис
 07 нояб. 2016 г., 17:48
Для других читателей: посмотрите на ответ @adamonduty для объяснения того, почему вы хотите поймать os.Interrupt и syscall.SIGTERM. Было бы неплохо включить его объяснение в этот ответ, особенно с тех пор, как он написал за несколько месяцев до вас.

Ваш ответ на вопрос