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

后端 未结 10 1087
抹茶落季
抹茶落季 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 15:48

    You can have a different goroutine that detects syscall.SIGINT and syscall.SIGTERM signals and relay them to a channel using signal.Notify. You can send a hook to that goroutine using a channel and save it in a function slice. When the shutdown signal is detected on the channel, you can execute those functions in the slice. This can be used to clean up the resources, wait for running goroutines to finish, persist data, or print partial run totals.

    I wrote a small and simple utility to add and run hooks at shutdown. Hope it can be of help.

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

    You can do this in a 'defer' fashion.

    example for shutting down a server gracefully :

    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()
    
    0 讨论(0)
  • 2020-11-29 15:49

    All of the above seem to work when spliced in, but gobyexample's signals page has a really clean and complete example of signal capturing. Worth adding to this list.

    0 讨论(0)
  • 2020-11-29 15:51

    Death is a simple library that uses channels and a wait group to wait for shutdown signals. Once the signal has been received it will then call a close method on all of your structs that you want to cleanup.

    0 讨论(0)
  • 2020-11-29 15:51

    This is another version which work in case you have some tasks to cleanup. Code will leave clean up process in their method.

    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
    }
    

    in case you need to clean main function you need to capture signal in main thread using go func() as well.

    0 讨论(0)
  • 2020-11-29 15:54

    Just for the record if somebody needs a way to handle signals on Windows. I had a requirement to handle from prog A calling prog B through os/exec but prog B never was able to terminate gracefully because sending signals through ex. cmd.Process.Signal(syscall.SIGTERM) or other signals are not supported on Windows. The way I handled is by creating a temp file as a signal ex. .signal.term through prog A and prog B needs to check if that file exists on interval base, if file exists it will exit the program and handle a cleanup if needed, I'm sure there are other ways but this did the job.

    0 讨论(0)
  • 2020-11-29 16:00

    You can use the os/signal package to handle incoming signals. Ctrl+C is SIGINT, so you can use this to trap 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
        }
    }()
    

    The manner in which you cause your program to terminate and print information is entirely up to you.

    0 讨论(0)
提交回复
热议问题