web-dev-qa-db-ja.com

「延期」方式でCtrl + C信号をキャプチャし、クリーンアップ機能を実行することは可能ですか?

キャプチャしたい Ctrl+C (SIGINT)コンソールから送信されたシグナル。一部の実行合計を出力します。

これはGolangで可能ですか?

注:私が最初に質問を投稿したとき、私は混乱していました Ctrl+C SIGTERMではなくSIGINTである。

178

os/signal パッケージを使用して、着信信号を処理できます。 ^ Cは SIGINT なので、これを使用して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
    }
}()

プログラムを終了して情報を印刷する方法は、完全にユーザー次第です。

228
Lily Ballard

これは動作します:

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

他の回答に少し追加するために、実際にSIGTERM(killコマンドによって送信されるデフォルトのシグナル)をキャッチしたい場合、os.Interruptの代わりにsyscall.SIGTERMを使用できます。 syscallインターフェースはシステム固有であり、どこでも機能しない可能性があることに注意してください(Windowsなど)。しかし、両方をキャッチするとうまくいきます:

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

上記の受け入れられた回答には(投稿の時点で)1つまたは2つの小さなタイプミスがあったため、ここにクリーンアップされたバージョンがあります。この例では、Ctrl + Cを受け取ったときにCPUプロファイラーを停止しています。

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

上記のすべては、接続されたときに機能するように見えますが、 gobyexampleのシグナルページ には、シグナルキャプチャの非常にクリーンで完全な例があります。このリストに追加する価値があります。

7
will-ob

これは、クリーンアップするタスクがある場合に機能する別のバージョンです。コードはメソッドにクリーンアッププロセスを残します。

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
}

メイン関数をクリーニングする必要がある場合は、go func()を使用してメインスレッドでシグナルをキャプチャする必要があります。

0
Hlex