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.

314voto

Kevin Ballard Points 88866

Vous pouvez utiliser le os/signal pour traiter les signaux entrants. Ctrl + C es SIGINT Vous pouvez donc l'utiliser pour piéger 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 manière dont vous faites en sorte que votre programme se termine et imprime des informations vous appartient entièrement.

0 votes

Je vois que vous appelez simplement Notify() au lieu de signal.Notify(). Est-ce que c'est la même chose ?

0 votes

@SebastiánGrignoli : Erreur de transcription. Taper directement du code dans SO ne conduit pas toujours à quelque chose qui peut effectivement être exécuté. Je vais corriger cela.

0 votes

Ok. J'ai demandé parce que j'ai vu ce genre d'appel (en omettant le préfixe) partout dans la documentation des bibliothèques. Je pense qu'ils font référence à l'appel de la fonction depuis l'intérieur des bibliothèques...

129voto

Barry Points 569

Ça marche :

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
    }
}

Passage en caisse dans la Terrain de jeux

1 votes

Pour les autres lecteurs : Regardez la réponse de @adamonduty pour savoir pourquoi vous voulez attraper os.Interrupt et syscall.SIGTERM. Il aurait été bien d'inclure son explication dans cette réponse, surtout qu'il a posté des mois avant vous.

1 votes

Pourquoi utilisez-vous un canal non bloquant ? Est-ce nécessaire ?

4 votes

@Barry pourquoi la taille du tampon est de 2 au lieu de 1 ?

30voto

adamonduty Points 1202

Pour compléter légèrement les autres réponses, si vous voulez réellement attraper SIGTERM (le signal par défaut envoyé par la commande kill), vous pouvez utiliser syscall.SIGTERM à la place de os.Interrupt. Attention, l'interface syscall est spécifique au système et peut ne pas fonctionner partout (par exemple sous Windows). Mais elle fonctionne bien pour attraper les deux :

c := make(chan os.Signal, 2)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
....

2 votes

Et os.Kill ?

2 votes

@Eclipse Grande question ! os.Kill correspond à a syscall.Kill qui est un signal qui peut être envoyé mais pas capté. C'est l'équivalent de la commande kill -9 <pid> . Si vous voulez attraper kill <pid> et s'éteindre de manière gracieuse, vous devez utiliser syscall.SIGTERM .

23voto

gravitron Points 1680

Il y avait (au moment de la publication) une ou deux petites fautes de frappe dans la réponse acceptée ci-dessus, voici donc la version corrigée. Dans cet exemple, j'arrête le profileur de CPU lorsque je reçois le message suivant 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)                                                     
  }                                                                
}()

4 votes

Notez que pour que la goroutine obtienne du temps processeur pour traiter le signal, la goroutine principale doit appeler une opération bloquante ou appeler runtime.Gosched à un endroit approprié (dans la boucle principale de votre programme, s'il en a une)

12voto

willscripted Points 1300

Tout ce qui précède semble fonctionner lorsqu'il est épissé, mais Page des signaux de gobyexample a un exemple vraiment propre et complet de capture de signal. Il mérite d'être ajouté à cette liste.

package main

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

func main() {
    sigs := make(chan os.Signal, 1)
    done := make(chan bool, 1)

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

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

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

Source : gobyexample.com/signals

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