콘솔에서 보낸 Ctrl+C( SIGINT
) 신호 를 캡처하고 부분 실행 합계를 인쇄하고 싶습니다 .
골랑에서 가능합니까?
참고 : 내가 먼저 질문을 게시 할 때 나는에 대해 혼란스러워했다 Ctrl+C되는 SIGTERM
대신 SIGINT
.
콘솔에서 보낸 Ctrl+C( SIGINT
) 신호 를 캡처하고 부분 실행 합계를 인쇄하고 싶습니다 .
골랑에서 가능합니까?
참고 : 내가 먼저 질문을 게시 할 때 나는에 대해 혼란스러워했다 Ctrl+C되는 SIGTERM
대신 SIGINT
.
답변:
os / signal 패키지를 사용하여 들어오는 신호를 처리 할 수 있습니다 . Ctrl+ 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
}
}()
프로그램을 종료하고 정보를 인쇄하는 방식은 전적으로 귀하에게 달려 있습니다.
go run
콘솔을 통해 프로그램을 실행하고 ^ C를 통해 SIGTERM을 보내면 신호가 채널에 기록되고 프로그램이 응답하지만 예기치 않게 루프에서 빠지는 것처럼 보입니다. SIGRERM도 사용하기 때문입니다 go run
! (이것은 나 원인 상당한 혼란이있다!)
runtime.Gosched
적절한 위치 (프로그램의 기본 루프에있는 경우)에서 호출해야합니다.
이것은 작동합니다 :
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
}
}
다른 답변에 약간 더하기 위해 실제로 SIGTERM (kill 명령으로 전송 된 기본 신호)을 잡으려면 syscall.SIGTERM
os.Interrupt 대신 사용할 수 있습니다 . syscall 인터페이스는 시스템에 따라 다르며 모든 곳에서 작동하지 않을 수 있습니다 (예 : Windows). 그러나 두 가지를 모두 잘 잡을 수 있습니다.
c := make(chan os.Signal, 2)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
....
signal.Notify
기능을 사용하면 한 번에 여러 신호를 지정할 수 있습니다. 따라서 코드를로 단순화 할 수 있습니다 signal.Notify(c, os.Interrupt, syscall.SIGTERM)
.
위의 허용 된 답변에 하나 또는 두 개의 작은 오타가 있었으므로 여기에 정리 된 버전이 있습니다. 이 예에서는 Ctrl+를 수신 할 때 CPU 프로파일 러를 중지합니다 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)
}
}()
runtime.Gosched
적절한 위치 (프로그램의 기본 루프에있는 경우)에서 호출해야합니다.
위의 모든 내용은 접속했을 때 작동하는 것처럼 보이지만 gobyexample의 신호 페이지 에는 신호를 캡처하는 데 대한 깨끗하고 완전한 예가 있습니다. 이 목록에 추가 할 가치가 있습니다.
Death 는 채널과 대기 그룹을 사용하여 종료 신호를 기다리는 간단한 라이브러리입니다. 신호가 수신되면 정리하려는 모든 구조체에서 close 메소드를 호출합니다.
당신은 syscall.SIGINT 및 syscall.SIGTERM 신호를 감지하는 다른 goroutine을 가지고 사용 채널로 중계 할 수 signal.Notify을 . 채널을 사용하여 해당 고 루틴에 후크를 보내고 함수 슬라이스에 저장할 수 있습니다. 채널에서 종료 신호가 감지되면 슬라이스에서 해당 기능을 실행할 수 있습니다. 자원을 정리하고 실행중인 goroutines가 완료 될 때까지 기다리거나 데이터를 유지하거나 부분 실행 합계를 인쇄하는 데 사용할 수 있습니다.
종료시 후크를 추가하고 실행하는 작고 간단한 유틸리티를 작성했습니다. 도움이 되길 바랍니다.
https://github.com/ankit-arora/go-utils/blob/master/go-shutdown-hook/shutdown-hook.go
이를 '지연'방식으로 수행 할 수 있습니다.
서버를 정상적으로 종료하는 예 :
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()
이것은 정리해야 할 작업이있는 경우 사용할 수있는 다른 버전입니다. 코드는 방법에 따라 정리 프로세스를 유지합니다.
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 ()을 사용하여 메인 스레드에서 신호를 캡처해야합니다.
누군가가 Windows에서 신호를 처리하는 방법이 필요한 경우에만 기록하십시오. os / exec를 통해 prog A를 호출하는 prog A에서 처리 해야하는 요구 사항이 있었지만 ex를 통해 신호를 전송하기 때문에 prog B는 결코 정상적으로 종료되지 못했습니다. cmd.Process.Signal (syscall.SIGTERM) 또는 기타 신호는 Windows에서 지원되지 않습니다. 내가 처리 한 방법은 신호 파일로 임시 파일을 만드는 것입니다. prog A와 prog B를 통한 .signal.term은 해당 파일이 간격베이스에 있는지 확인해야합니다. 파일이 존재하면 파일이 프로그램을 종료하고 필요한 경우 정리를 처리합니다. 다른 방법이 있지만 확실하게 작동합니다.