Czy można przechwycić sygnał Ctrl + C i uruchomić funkcję czyszczenia, w sposób "odrocz"?

Chcę przechwycić Ctrl + C (SIGINT) sygnał wysyłany z konsoli i wypisuje częściowe sumy uruchomień.

Czy to możliwe w Golang?

Uwaga: kiedy po raz pierwszy napisałem pytanie, byłem zdezorientowany, że Ctrl+C jest SIGTERM zamiast SIGINT.

Author: Flimzy, 2012-06-30

9 answers

Możesz użyć pakietu os/signal do obsługi przychodzących sygnałów. ^C to SIGINT , więc możesz użyć tego do pułapki 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
    }
}()
Sposób, w jaki spowodujesz zakończenie programu i wydrukowanie informacji, zależy wyłącznie od Ciebie.
 203
Author: Kevin Ballard,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-24 20:53:44

To działa:

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
    }
}
 67
Author: Barry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-22 23:29:30

Aby dodać nieco do pozostałych odpowiedzi, jeśli rzeczywiście chcesz złapać SIGTERM (domyślny sygnał wysyłany przez polecenie kill), możesz użyć syscall.SIGTERM zamiast systemu operacyjnego.Przerywam. Uwaga: interfejs syscall jest specyficzny dla systemu i może nie działać wszędzie (np. w systemie windows). Ale fajnie jest złapać oba:

c := make(chan os.Signal, 2)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
....
 24
Author: adamlamar,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-08-25 19:55:12

W zaakceptowanej odpowiedzi powyżej była (w momencie pisania postu) jedna lub dwie małe literówki, więc oto wersja oczyszczona. W tym przykładzie Zatrzymuję CPU profiler po otrzymaniu 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)                                                     
  }                                                                
}()    
 16
Author: gravitron,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-09-25 01:36:16

Wszystkie powyższe wydają się działać, gdy są połączone, ale strona sygnałów gobyexample ma naprawdę czysty i kompletny przykład przechwytywania sygnału. Warto dodać do tej listy.

 6
Author: will-ob,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-02-20 13:01:31

Jest to kolejna wersja, która działa w przypadku, gdy masz jakieś zadania do oczyszczenia. Kod pozostawi proces czyszczenia w swojej metodzie.

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
}

Jeśli chcesz wyczyścić główną funkcję, musisz przechwycić sygnał w głównym wątku za pomocą go func ().

 0
Author: Hlex,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-20 00:52:29

Death jest prostą biblioteką, która używa kanałów i grupy oczekiwania do oczekiwania na sygnały zamknięcia. Po odebraniu sygnału wywoła metodę close na wszystkich Twoich strukturach, które chcesz wyczyścić.

 -1
Author: Ben Aldrich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-24 03:06:35

Możesz mieć inny goroutine, który wykrywa syscall.SIGINT i syscall.Sygnały SIGTERM i przekazują je do kanału za pomocą sygnału ./ Align = "left" / Możesz wysłać hook do tego goroutine za pomocą kanału i zapisać go w plasterku funkcji. Po wykryciu sygnału wyłączenia na kanale można wykonać te funkcje w plasterku. Może to być używane do czyszczenia zasobów, oczekiwania na uruchomienie goroutines, przechowywania danych lub drukowania częściowych sumie uruchomień.

Napisałem mały i proste narzędzie do dodawania i uruchamiania hooków przy zamykaniu. Mam nadzieję, że to może pomóc.

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

Możesz to zrobić w sposób "odroczony".

Przykład wyłączenia serwera:

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()
 -1
Author: Ankit Arora,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-09 18:45:31

Tak dla przypomnienia, jeśli ktoś potrzebuje sposobu na obsługę sygnałów w Windows. MiaĹ 'em Wymaganie obsĹ' ugi z prog a wywoĹ 'ania prog B Przez os/exec, ale prog B nigdy nie byĹ' w stanie zakoĹ "czyÄ ‡ z gracjÄ..., poniewaĹź wysyĹ 'anie sygnaĹ' Ăłw przez ex. cmd.Proces.Signal (syscall.SIGTERM) lub inne sygnały nie są obsługiwane w systemie Windows. Sposób, w jaki sobie radziłem, polega na utworzeniu pliku tymczasowego jako sygnału ex. .sygnał.term poprzez prog a i prog B musi sprawdzić, czy ten plik istnieje na bazie interwałów, jeśli plik istnieje, to WYJDŹ z programu i zajmij się czyszczeniem w razie potrzeby, jestem pewien, że są inne sposoby, ale to wykonało zadanie.

 -2
Author: user212806,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-10-02 17:06:44