web-dev-qa-db-fra.com

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

Je veux capturer le Ctrl+C (SIGINT) signal envoyé depuis la console et affiche certains totaux partiels.

Est-ce possible à Golang?

Remarque: lorsque j'ai posté la question pour la première fois, j'étais confus au sujet de Ctrl+C étant SIGTERM au lieu de SIGINT.

178

Vous pouvez utiliser le paquet os/signal pour gérer les signaux entrants. ^ C est SIGINT , vous pouvez donc utiliser ceci pour intercepter 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.

220
Lily Ballard

Cela 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
    }
}
76
Barry

Pour ajouter un peu aux 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. Notez que l’interface syscall est spécifique au système et risque de ne pas fonctionner partout (par exemple sous Windows). Mais cela fonctionne bien pour attraper les deux:

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

Il y avait (au moment de la publication) une ou deux petites fautes de frappe dans la réponse acceptée ci-dessus, alors voici la version corrigée. Dans cet exemple, j'arrête le profileur de la CPU lors de la réception de 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)                                                     
  }                                                                
}()    
17
gravitron

Tout ce qui précède semble fonctionner une fois raccordé, mais la page des signaux de gobyexample présente un exemple très net et complet de capture de signal. Cela vaut la peine d’être ajouté à cette liste.

6
will-ob

C'est une autre version qui fonctionne dans le cas où vous auriez des tâches à nettoyer. Code laissera le processus de nettoyage dans leur 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
}

si vous avez besoin de nettoyer la fonction principale, vous devez également capturer le signal dans le thread principal en utilisant go func ().

0
Hlex