क्या एक Ctrl + C सिग्नल को कैप्चर करना और "डेफर" फैशन में एक क्लीनअप फ़ंक्शन चलाना संभव है?


207

मैं कब्जा करना चाहता हूं Ctrl+CSIGINT कंसोल से भेजे ( ) सिग्नल और कुछ आंशिक रन योग प्रिंट करता ।

क्या गोलंग में यह संभव है?

नोट: जब मैंने पहली बार प्रश्न पोस्ट किया था तो मैं इसके बजाय भ्रमित Ctrl+Cहोने के बारे में था ।SIGTERMSIGINT

जवाबों:


260

आप आने वाले संकेतों को संभालने के लिए ओएस / सिग्नल पैकेज का उपयोग कर सकते हैं । 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
    }
}()

जिस तरह से आप अपने कार्यक्रम को समाप्त करने और जानकारी प्रिंट करने के लिए कहते हैं, वह पूरी तरह से आपके ऊपर है।


धन्यवाद! तो ... ^ यह SIGTERM नहीं है, तब? अद्यतन: क्षमा करें, आपके द्वारा प्रदान किया गया लिंक पर्याप्त विस्तृत है!
सेबेस्टियन ग्रिग्नोली

19
इसके बजाय for sig := range g {, आप <-sigchanइस पिछले उत्तर में भी उपयोग कर सकते हैं : stackoverflow.com/questions/8403862/…
Denys Séguret

3
@ डिस्ट्रॉय: यकीन है, अगर आप वास्तव में पहले सिग्नल के जवाब में प्रोग्राम को समाप्त करने जा रहे हैं। यदि आप प्रोग्राम को समाप्त नहीं करने का निर्णय लेते हैं तो लूप का उपयोग करके आप सभी संकेतों को पकड़ सकते हैं ।
लिली बॉलर

79
नोट: आपको वास्तव में काम करने के लिए इस कार्यक्रम का निर्माण करना चाहिए। यदि आप प्रोग्राम को go runकंसोल में चलाते हैं और ^ C के माध्यम से एक SIGTERM भेजते हैं, तो सिग्नल चैनल में लिखा जाता है और प्रोग्राम प्रतिक्रिया देता है, लेकिन अप्रत्याशित रूप से लूप से बाहर निकलता है। इसका कारण यह है कि SIGRERM भी जाता है go run! (इससे मुझे काफी भ्रम हो गया!)
विलियम पर्ससेल ने

5
ध्यान दें कि सिग्नल को संभालने के लिए गोरोइन के लिए प्रोसेसर समय प्राप्त करने के लिए, मुख्य गोरोइन को एक अवरुद्ध ऑपरेशन को कॉल करना होगा या runtime.Goschedएक उपयुक्त स्थान पर कॉल करना चाहिए (आपके कार्यक्रम के मुख्य लूप में, यदि यह एक है)
misterbee

107

यह काम:

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

1
अन्य पाठकों के लिए: एक स्पष्टीकरण के लिए @ademontuty के उत्तर को देखें कि आप os.Interrupt और syscall.SIGTERM को क्यों पकड़ना चाहते हैं, इस उत्तर में उनका स्पष्टीकरण शामिल करना अच्छा होगा, खासकर जब से वह आपके द्वारा महीनों पहले पोस्ट किया गया हो।
क्रिस

1
आप एक गैर-अवरुद्ध चैनल का उपयोग क्यों कर रहे हैं? क्या यह आवश्यक है?
शाम

4
@ क्यों बफर 1 के बजाय 2 आकार है?
पीड़वा

2
यहाँ प्रलेखन से एक अंश है । "पैकेज सिग्नल सी को भेजना बंद नहीं करेगा: कॉलर को यह सुनिश्चित करना होगा कि सी के पास अपेक्षित सिग्नल दर के साथ रखने के लिए पर्याप्त बफर स्थान है। सिर्फ एक सिग्नल मूल्य की अधिसूचना के लिए उपयोग किए जाने वाले चैनल के लिए, आकार 1 का बफर पर्याप्त है।"
बीएमडेलैक्रूज़

25

अन्य उत्तरों में थोड़ा जोड़ने के लिए, यदि आप वास्तव में SIGTERM (किल कमांड द्वारा भेजा गया डिफ़ॉल्ट संकेत) को पकड़ना चाहते हैं, तो आप उपयोग कर सकते हैं syscall.SIGTERM os.Interrupt के स्थान पर । खबरदार कि syscall इंटरफ़ेस सिस्टम-विशिष्ट है और हर जगह काम नहीं कर सकता है (उदाहरण के लिए विंडोज़)। लेकिन यह अच्छी तरह से दोनों को पकड़ने के लिए काम करता है:

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

7
signal.Notifyसमारोह एक बार में कई संकेतों को निर्दिष्ट करने की अनुमति देता है। इस प्रकार, आप अपने कोड को सरल बना सकते हैं signal.Notify(c, os.Interrupt, syscall.SIGTERM)
जोचेन

मुझे लगता है कि मैंने पाया कि पोस्टिंग के बाद। फिक्स्ड!
adamlamar

2
Os.Kill के बारे में क्या?
शाम

2
@ ग्रहण बड़ा सवाल! os.Kill से मेल खाती है syscall.Kill, जो एक संकेत है जिसे भेजा जा सकता है लेकिन पकड़ा नहीं जाता। इसके कमांड के बराबर kill -9 <pid>। यदि आप kill <pid>आकर्षक रूप से शटडाउन पकड़ना चाहते हैं , तो आपको इसका उपयोग करना होगा syscall.SIGTERM
अदमलार

@adamlamar आह, यह समझ में आता है। धन्यवाद!
Awn

18

ऊपर दिए गए उत्तर में एक (दो पोस्टिंग के समय) एक या दो छोटे टाइपो थे, इसलिए यहाँ साफ किया गया संस्करण है। इस उदाहरण में मैं 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)                                                     
  }                                                                
}()    

2
ध्यान दें कि सिग्नल को संभालने के लिए गोरोइन के लिए प्रोसेसर का समय मिलता है, मुख्य गोरोइन को एक अवरुद्ध ऑपरेशन को कॉल करना चाहिए या runtime.Goschedएक उपयुक्त स्थान पर कॉल करना चाहिए (आपके प्रोग्राम के मुख्य लूप में, यदि यह एक है)
misterbee

8

उपरोक्त सभी काम करते हैं जब spliced ​​में दिखाई देते हैं, लेकिन gobyexample के सिग्नल पृष्ठ में सिग्नल कैप्चरिंग का वास्तव में स्वच्छ और पूर्ण उदाहरण है। इस सूची में जोड़ने के लायक।


0

मृत्यु एक साधारण पुस्तकालय है जो शटडाउन संकेतों के लिए प्रतीक्षा करने के लिए चैनलों और एक प्रतीक्षा समूह का उपयोग करता है। एक बार जब संकेत प्राप्त हो जाता है तो यह आपके सभी संरूपों पर एक करीबी विधि कहेगा जिसे आप साफ करना चाहते हैं।


3
कोड की इतनी सारी पंक्तियाँ और एक बाहरी पुस्तकालय इस बात के लिए निर्भर है कि कोड की चार पंक्तियों में क्या किया जा सकता है? (स्वीकृत उत्तर के अनुसार)
जैकब

यदि आप मानक क्लियर इंटरफ़ेस रखते हैं तो यह आपको समानांतर और स्वचालित रूप से करीबी संरचना में उन सभी की सफाई करने की अनुमति देता है।
बेन एल्ड्रिच

0

आपके पास एक अलग गोरोइन हो सकता है जो syscall.SIGINT और syscall.SIGTERM संकेतों का पता लगाता है और सिग्नल का उपयोग करके उन्हें एक चैनल में रिले करता है । नोटिफाइ करें । आप एक चैनल का उपयोग करके उस गोरोइन को एक हुक भेज सकते हैं और इसे फ़ंक्शन स्लाइस में सहेज सकते हैं। जब चैनल पर शटडाउन सिग्नल का पता चलता है, तो आप स्लाइस में उन फ़ंक्शन को निष्पादित कर सकते हैं। इसका उपयोग संसाधनों को साफ करने के लिए किया जा सकता है, गोरोइन को चलाने के लिए प्रतीक्षा करें, डेटा को बनाए रखने या आंशिक रन योग को प्रिंट करने के लिए।

मैंने शटडाउन पर हुक जोड़ने और चलाने के लिए एक छोटी और सरल उपयोगिता लिखी। आशा है कि यह मदद की हो सकती है।

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()

0

यह एक और संस्करण है जो उस स्थिति में काम करता है जब आपके पास सफाई के लिए कुछ कार्य हैं। कोड उनकी विधि में सफाई प्रक्रिया को छोड़ देगा।

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
}

यदि आपको मुख्य फ़ंक्शन को साफ करने की आवश्यकता है, तो आपको मुख्य फ़ेंक () के साथ-साथ मुख्य थ्रेड का उपयोग करके सिग्नल पर कब्जा करने की आवश्यकता है।


-2

बस रिकॉर्ड के लिए अगर किसी को विंडोज पर सिग्नल को संभालने का तरीका चाहिए। मुझे ओएस / एग्जीक्यूटिव के माध्यम से कॉलिंग प्रोग बी से निपटने की आवश्यकता थी लेकिन प्रोग बी कभी भी सुंदर तरीके से समाप्त करने में सक्षम नहीं था क्योंकि पूर्व के माध्यम से संकेत भेज रहा था। cmd.Process.Signal (syscall.SIGTERM) या अन्य सिग्नल विंडोज पर समर्थित नहीं हैं। जिस तरह से मैंने संभाला है वह एक सिग्नल पूर्व के रूप में एक अस्थायी फ़ाइल बना रहा है। प्रोग ए। और प्रोग बी के माध्यम से .signal.term की जाँच करने की आवश्यकता है कि क्या फ़ाइल अंतराल आधार पर मौजूद है, यदि फ़ाइल मौजूद है तो यह प्रोग्राम से बाहर निकल जाएगी और यदि आवश्यक हो तो सफाई को संभाल लेगी, मुझे यकीन है कि अन्य तरीके भी हैं लेकिन यह काम किया है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.