Я хочу захватить сигнал Ctrl+C (SIGINT), отправленный с консоли, и распечатать некоторые частичные итоги выполнения.
Возможно ли это в Голанге?
Примечание. Когда я впервые разместил вопрос, я смутился о Ctrl+C как SIGTERM вместо SIGINT.
Я хочу захватить сигнал Ctrl+C (SIGINT), отправленный с консоли, и распечатать некоторые частичные итоги выполнения.
Возможно ли это в Голанге?
Примечание. Когда я впервые разместил вопрос, я смутился о Ctrl+C как SIGTERM вместо SIGINT.
Вы можете использовать пакет 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, 2)
    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 вместо os.Interrupt. Помните, что интерфейс syscall является системным и может работать не везде (например, в окнах). Но он отлично работает, чтобы поймать оба:
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 имеет действительно чистый и полный пример захвата сигнала. Стоит добавить в этот список.
Death - это простая библиотека, которая использует каналы и группу ожидания для ожидания сигналов выключения. Как только сигнал будет получен, он вызовет метод close для всех ваших структур, которые вы хотите очистить.
Это еще одна версия, которая работает, если у вас есть некоторые задачи для очистки. Код оставит процесс очистки в своем методе.
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
}
если вам нужно очистить основную функцию, вам необходимо захватить сигнал в основном потоке, используя функцию fun().
У вас может быть другой goroutine, который обнаруживает сигналы syscall.SIGINT и syscall.SIGTERM и передает их на канал с помощью signal.Notify. Вы можете отправить крючок на этот канал с помощью канала и сохранить его в срезе функции. Когда сигнал отключения обнаружен на канале, вы можете выполнять эти функции в срезе. Это можно использовать, чтобы очистить ресурсы, дождаться завершения работы goroutines, сохранить данные или распечатать частичные итоги выполнения.
Я написал небольшую и простую утилиту для добавления и запуска перехватов при завершении работы. Надеюсь, это поможет.
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. Вызов prog B через os/exec, но prog B никогда не мог прекратить изящество, потому что отправлял сигналы через ex. cmd.Process.Signal(syscall.SIGTERM) или другие сигналы не поддерживаются в Windows. То, как я обрабатывал, - это создать временный файл как сигнал ex..signal.term через prog A и prog B необходимо проверить, существует ли этот файл на базе интервалов, если файл существует, он выйдет из программы и обработает очистку, если это необходимо, я уверен, что есть другие способы, но это выполнило эту работу.