Is it possible to capture a Ctrl+C signal and run a cleanup function, in a “defer” fashion?

后端 未结 10 1088
抹茶落季
抹茶落季 2020-11-29 15:30

I want to capture the Ctrl+C (SIGINT) signal sent from the console and print out some partial run totals.

Is this possible in Golang?

相关标签:
10条回答
  • 2020-11-29 16:01

    This works:

    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
        }
    }
    
    0 讨论(0)
  • 2020-11-29 16:09

    To add slightly to the other answers, if you actually want to catch SIGTERM (the default signal sent by the kill command), you can use syscall.SIGTERM in place of os.Interrupt. Beware that the syscall interface is system-specific and might not work everywhere (e.g. on windows). But it works nicely to catch both:

    c := make(chan os.Signal, 2)
    signal.Notify(c, os.Interrupt, syscall.SIGTERM)
    ....
    
    0 讨论(0)
  • 2020-11-29 16:10

    look at the example

    When we run this program it will block waiting for a signal. By typing ctrl-C (which the terminal shows as ^C) we can send a SIGINT signal, causing the program to print interrupt and then exit.

    signal. Notify registers the given channel to receive notifications of the specified signals.

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

    detect HTTP request cancel

    
    
    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)
    
    }
    
    0 讨论(0)
  • 2020-11-29 16:14

    There were (at time of posting) one or two little typos in the accepted answer above, so here's the cleaned up version. In this example I'm stopping the CPU profiler when receiving 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)                                                     
      }                                                                
    }()    
    
    0 讨论(0)
提交回复
热议问题