妄徒之命
2012-06-29 21:05
采纳率: 25%
浏览 222

是否有可能捕捉 Ctrl + c 信号并以"defer"的方式运行一个清理函数?

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?

Note: When I first posted the question I was confused about Ctrl+C being SIGTERM instead of SIGINT.

转载于:https://stackoverflow.com/questions/11268943/is-it-possible-to-capture-a-ctrlc-signal-and-run-a-cleanup-function-in-a-defe

  • 写回答
  • 关注问题
  • 收藏
  • 邀请回答

9条回答 默认 最新

  • Lotus@ 2012-06-29 21:16
    已采纳

    You can use the os/signal package to handle incoming signals. ^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.

    打赏 评论
  • 10.24 2012-09-24 18:55

    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)                                                     
      }                                                                
    }()    
    
    打赏 评论
  • local-host 2013-05-14 08:08

    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)
    ....
    
    打赏 评论
  • YaoRaoLov 2013-08-10 05:15

    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
        }
    }
    
    打赏 评论
  • 狐狸.fox 2015-02-20 13:01

    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.

    打赏 评论
  • MAO-EYE 2015-03-24 03:06

    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.

    打赏 评论
  • lrony* 2017-07-09 17:59

    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()
    
    打赏 评论
  • 笑故挽风 2017-10-02 17:06

    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.

    打赏 评论
  • 喵-见缝插针 2017-11-20 00:52

    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.

    打赏 评论

相关推荐 更多相似问题