256 votes

Est-il possible de capturer un signal Ctrl+C (SIGINT) et d'exécuter une fonction de nettoyage, de manière "différée" ?

Je veux capturer le Ctrl+C ( SIGINT ) envoyé par la console et d'imprimer des totaux partiels d'exécution.

2voto

dılo sürücü Points 1989

regardez l'exemple

Lorsque nous lançons ce programme, il se bloque en attendant un signal. En tapant ctrl-C (que le terminal affiche comme ^C), nous pouvons envoyer un signal SIGINT, provoquant l'interruption du programme et sa sortie.

signal. Notify enregistre le canal donné pour recevoir les notifications des signaux spécifiés.

package main

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

func main() {

    sig := make(chan os.Signal, 1)
    done := make(chan bool, 1)

    signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)

    go func() {
        sig := <-sig
        fmt.Println()
        fmt.Println(sig)
        done <- true

        fmt.Println("ctrl+c")
    }()

    fmt.Println("awaiting signal")
    <-done
    fmt.Println("exiting")
}

détecter l'annulation d'une requête HTTP

package main

import (
    "fmt"
    "net/http"
    "time"
)

func main() {

    mux := http.NewServeMux()
    mux.HandleFunc("/path", func(writer http.ResponseWriter, request *http.Request) {

        time.Sleep(time.Second * 5)

        select {
        case <-time.After(time.Millisecond * 10):

            fmt.Println("started")
            return
        case <-request.Context().Done():
            fmt.Println("canceled")
        }
    })

    http.ListenAndServe(":8000", mux)

}

0voto

Ankit Arora Points 194

Vous pouvez avoir une goroutine différente qui détecte les signaux syscall.SIGINT et syscall.SIGTERM et les relaie à un canal en utilisant signal.Notifier . Vous pouvez envoyer un hook à cette goroutine en utilisant un canal et le sauvegarder dans une tranche de fonction. Lorsque le signal d'arrêt est détecté sur le canal, vous pouvez exécuter ces fonctions dans la tranche. Cela peut être utilisé pour nettoyer les ressources, attendre que les goroutines en cours d'exécution se terminent, persister les données ou imprimer les totaux d'exécution partielle.

J'ai écrit un petit utilitaire simple pour ajouter et exécuter des hooks à l'arrêt. J'espère qu'il pourra vous être utile.

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

Vous pouvez le faire de manière différée.

exemple d'arrêt d'un serveur en douceur :

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

0voto

Hlex Points 56

Voici une autre version qui fonctionne si vous avez des tâches à nettoyer. Le code laissera le processus de nettoyage dans sa méthode.

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
}

dans le cas où vous devez nettoyer la fonction principale, vous devez capturer le signal dans le thread principal en utilisant aussi go func().

-1voto

Ben Aldrich Points 83

Décès est une bibliothèque simple qui utilise des canaux et un groupe d'attente pour attendre les signaux d'arrêt. Une fois que le signal a été reçu, elle appelle une méthode de fermeture sur toutes les structures que vous voulez nettoyer.

-4voto

user212806 Points 13

Juste pour mémoire, si quelqu'un a besoin d'un moyen de gérer les signaux sous Windows.

J'avais besoin de gérer un programme A qui appelait un programme B par le biais de os/exec, mais le programme B n'a jamais été en mesure de se terminer gracieusement car l'envoi de signaux par le biais de cmd.Process.Signal(syscall.SIGTERM) ou d'autres signaux ne sont pas pris en charge par Windows.

J'ai traité ce problème en créant un fichier temporaire comme signal. Par exemple, le programme A crée le fichier .signal.term et le programme B doit vérifier si ce fichier existe sur la base d'intervalle. Si le fichier existe, il quittera le programme et effectuera un nettoyage si nécessaire.

Je suis sûr qu'il y a d'autres moyens, mais celui-ci a fait l'affaire.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X