¿Es posible capturar una señal Ctrl + C y ejecutar una función de limpieza, de manera "diferir"?


Quiero capturar el Ctrl + C (SIGINT) señal enviada desde la consola e imprimir algunos totales parciales.

¿Es esto posible en Golang?

Nota: Cuando publiqué por primera vez la pregunta, estaba confundido acerca de que Ctrl+C era SIGTERM en lugar de SIGINT.

Author: Flimzy, 2012-06-30

9 answers

Puede usar el paquete os/signal para manejar las señales entrantes. ^C es SIGINT , así que puedes usar esto para atrapar 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
    }
}()

La manera en la que haga que su programa termine e imprima información depende completamente de usted.

 203
Author: Kevin Ballard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-24 20:53:44

Esto funciona:

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
    }
}
 67
Author: Barry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-22 23:29:30

Para agregar un poco a las otras respuestas, si realmente desea capturar SIGTERM (la señal predeterminada enviada por el comando kill), puede usar syscall.SIGTERM en lugar de os.Interrumpir. Tenga en cuenta que la interfaz syscall es específica del sistema y podría no funcionar en todas partes (por ejemplo, en Windows). Pero funciona muy bien para atrapar ambos:

c := make(chan os.Signal, 2)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
....
 24
Author: adamlamar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-08-25 19:55:12

Hubo (en el momento de la publicación) uno o dos pequeños errores tipográficos en la respuesta aceptada anteriormente, así que aquí está la versión limpia. En este ejemplo, estoy deteniendo el generador de perfiles de CPU cuando recibo 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)                                                     
  }                                                                
}()    
 16
Author: gravitron,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-09-25 01:36:16

Todo lo anterior parece funcionar cuando se empalma, pero la página de señales de gobyexample tiene un ejemplo realmente limpio y completo de captura de señales. Vale la pena añadir a esta lista.

 6
Author: will-ob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-02-20 13:01:31

Esta es otra versión que funciona en caso de que tenga algunas tareas que limpiar. El código dejará el proceso de limpieza en su método.

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
}

En caso de que necesite limpiar la función principal, también necesita capturar la señal en el hilo principal usando go func ().

 0
Author: Hlex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-11-20 00:52:29

Death es una biblioteca simple que utiliza canales y un grupo de espera para esperar las señales de apagado. Una vez que se haya recibido la señal, llamará a un método de cierre en todas las estructuras que desee limpiar.

 -1
Author: Ben Aldrich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-24 03:06:35

Puede tener un goroutine diferente que detecta syscall.SIGINT y syscall.SIGTERM señala y retransmite a un canal usando la señal .Notificar. Puedes enviar un hook a ese goroutine usando un canal y guardarlo en un segmento de función. Cuando se detecta la señal de apagado en el canal, puede ejecutar esas funciones en el sector. Esto se puede usar para limpiar los recursos, esperar a que finalice la ejecución de goroutines, persistir los datos o imprimir totales de ejecuciones parciales.

Escribí un pequeño y simple utilidad para agregar y ejecutar ganchos al apagar. Espero que pueda ser de ayuda.

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

Puedes hacer esto de una manera 'diferir'.

Ejemplo para apagar un servidor con gracia :

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()
 -1
Author: Ankit Arora,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-09 18:45:31

Solo para el registro si alguien necesita una forma de manejar las señales en Windows. Tenía un requisito para manejar desde prog A llamando a prog B a través de os / exec, pero prog B nunca fue capaz de terminar con gracia porque el envío de señales a través de ex. cmd.Proceso.Señal (syscall.SIGTERM) u otras señales no son compatibles con Windows. La forma en que manejé es mediante la creación de un archivo temporal como una señal ex. .señal.término a través de prog A y prog B necesita comprobar si ese archivo existe en base de intervalo, si el archivo existe salir del programa y manejar una limpieza si es necesario, estoy seguro de que hay otras maneras, pero esto hizo el trabajo.

 -2
Author: user212806,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-02 17:06:44