Tôi muốn thu tín hiệu Ctrl+C( SIGINT
) được gửi từ bàn điều khiển và in ra một số tổng số chạy một phần.
Điều này có thể ở Golang không?
Lưu ý: Khi tôi lần đầu tiên đăng câu hỏi, tôi đã bối rối về Ctrl+Cviệc SIGTERM
thay vì SIGINT
.
Tôi muốn thu tín hiệu Ctrl+C( SIGINT
) được gửi từ bàn điều khiển và in ra một số tổng số chạy một phần.
Điều này có thể ở Golang không?
Lưu ý: Khi tôi lần đầu tiên đăng câu hỏi, tôi đã bối rối về Ctrl+Cviệc SIGTERM
thay vì SIGINT
.
Câu trả lời:
Bạn có thể sử dụng gói os / signal để xử lý các tín hiệu đến. Ctrl+ Clà SIGINT , vì vậy bạn có thể sử dụng cái này để bẫy 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
}
}()
Cách thức khiến chương trình của bạn chấm dứt và in thông tin hoàn toàn phụ thuộc vào bạn.
for sig := range g {
, bạn cũng có thể sử dụng <-sigchan
như trong câu trả lời trước: stackoverflow.com/questions/8403862/iêu
go run
bàn điều khiển và gửi SIGTERM qua ^ C, tín hiệu được ghi vào kênh và chương trình sẽ phản hồi, nhưng dường như bất ngờ rơi ra khỏi vòng lặp. Điều này là do SIGRERM go run
cũng vậy! (Điều này đã gây cho tôi sự nhầm lẫn đáng kể!)
runtime.Gosched
ở một nơi thích hợp (trong vòng lặp chính của chương trình của bạn, nếu có)
Những công việc này:
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
}
}
Để thêm một chút vào các câu trả lời khác, nếu bạn thực sự muốn bắt SIGTERM (tín hiệu mặc định được gửi bởi lệnh kill), bạn có thể sử dụng syscall.SIGTERM
thay cho os.Interrupt. Xin lưu ý rằng giao diện tòa nhà là dành riêng cho hệ thống và có thể không hoạt động ở mọi nơi (ví dụ: trên windows). Nhưng nó hoạt động độc đáo để bắt cả hai:
c := make(chan os.Signal, 2)
signal.Notify(c, os.Interrupt, syscall.SIGTERM)
....
signal.Notify
chức năng cho phép để xác định một số tín hiệu cùng một lúc. Vì vậy, bạn có thể đơn giản hóa mã của bạn để signal.Notify(c, os.Interrupt, syscall.SIGTERM)
.
Có (tại thời điểm đăng) một hoặc hai lỗi chính tả trong câu trả lời được chấp nhận ở trên, vì vậy đây là phiên bản đã được dọn sạch. Trong ví dụ này, tôi dừng trình cấu hình CPU khi nhận 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)
}
}()
runtime.Gosched
ở một nơi thích hợp (trong vòng lặp chính của chương trình của bạn, nếu có)
Tất cả những điều trên dường như hoạt động khi được ghép vào, nhưng trang tín hiệu của gobyexample có một ví dụ thực sự rõ ràng và đầy đủ về việc thu tín hiệu. Đáng để thêm vào danh sách này.
Cái chết là một thư viện đơn giản sử dụng các kênh và một nhóm chờ để chờ tín hiệu tắt máy. Khi tín hiệu đã được nhận, nó sẽ gọi một phương thức đóng trên tất cả các cấu trúc của bạn mà bạn muốn dọn dẹp.
Bạn có thể có một con goroutine khác nhau để phát hiện các tòa nhà cao tầng.SIGINT và syscall.SIGTERM và chuyển chúng đến một kênh bằng cách sử dụng signal.Notify . Bạn có thể gửi một cái móc đến con goroutine đó bằng cách sử dụng một kênh và lưu nó trong một lát chức năng. Khi tín hiệu tắt được phát hiện trên kênh, bạn có thể thực hiện các chức năng đó trong lát cắt. Điều này có thể được sử dụng để dọn sạch các tài nguyên, chờ cho chạy goroutines để hoàn thành, duy trì dữ liệu hoặc in tổng số chạy một phần.
Tôi đã viết một tiện ích nhỏ và đơn giản để thêm và chạy hook khi tắt máy. Hy vọng nó có thể giúp đỡ.
https://github.com/ankit-arora/go-utils/blob/master/go-shutdown-hook/shutdown-hook.go
Bạn có thể làm điều này theo cách 'trì hoãn'.
ví dụ để tắt máy chủ một cách duyên dáng:
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()
Đây là một phiên bản khác hoạt động trong trường hợp bạn có một số nhiệm vụ cần dọn dẹp. Mã sẽ để lại quá trình làm sạch trong phương pháp của họ.
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
}
trong trường hợp bạn cần làm sạch chức năng chính, bạn cũng cần thu tín hiệu trong luồng chính bằng cách sử dụng go func ().
Chỉ dành cho bản ghi nếu ai đó cần một cách để xử lý tín hiệu trên Windows. Tôi có một yêu cầu xử lý từ prog A gọi prog B thông qua os / exec nhưng prog B không bao giờ có thể chấm dứt một cách duyên dáng vì gửi tín hiệu qua ex. cmd.Process.Signal (syscall.SIGTERM) hoặc các tín hiệu khác không được hỗ trợ trên Windows. Cách tôi xử lý là bằng cách tạo tệp tạm thời dưới dạng tín hiệu cũ. .signal.term thông qua prog A và prog B cần kiểm tra xem tệp đó có tồn tại trên cơ sở khoảng không, nếu tệp tồn tại, nó sẽ thoát khỏi chương trình và xử lý dọn dẹp nếu cần, tôi chắc chắn có những cách khác nhưng điều này đã làm được.