Go में Ctrl+C (SIGINT) को पकड़ना: defer क्लीनअप और अंदरूनी काम

शुरुआती लोगों के लिए ऑनलाइन Python प्रशिक्षक

Python आसानी से सीखें बिना सिद्धांत के बोझ के। स्वचालित जांच के साथ व्यावहारिक कार्य हल करें और ब्राउज़र में सीधे कोड लिखें।

पाठ्यक्रम शुरू करें

क्या Go में Ctrl+C को पकड़कर defer की तरह क्लीनअप करना संभव है? अंदरूनी विश्लेषण

\n\n

क्या आपने कभी Go में सर्वर चलाते समय Ctrl+C दबाया और सोचा: "क्या हो अगर बाहर निकलने से पहले डेटाबेस कनेक्शन बंद कर सकूं, कैश सेव कर सकूं, या बस 'अलविदा' कह सकूं?" यह सवाल Stack Overflow पर इतना लोकप्रिय है (हज़ारों व्यूज़!) कि हमने इसे विस्तार से समझाने का फैसला किया। आज हम सिर्फ एक रेडीमेड रेसिपी नहीं देंगे, बल्कि अंदर की बात समझेंगे - Go सिग्नल कैसे हैंडल करता है, signal.Notify क्या है, और defer का इससे क्या संबंध है।

\n\n

तैयार हैं? चलिए शुरू करते हैं!

\n\n

समस्या: Ctrl+C प्रोग्राम को तुरंत मार देता है

\n\n

डिफ़ॉल्ट रूप से, जब आप टर्मिनल में Ctrl+C दबाते हैं, तो OS प्रक्रिया को SIGINT सिग्नल भेजता है। Go प्रोग्राम, अगर कुछ न किया जाए, तो अचानक बंद हो जाता है - सभी goroutines कट जाती हैं, defer फंक्शन नहीं चलते। यह बिजली का प्लग खींचने जैसा है: कंप्यूटर बंद हो जाएगा, लेकिन डेटा खो सकता है।

\n\n

क्लासिक उदाहरण: आप एक वेब सर्वर लिख रहे हैं, PostgreSQL से कनेक्शन खोला है, और अचानक - Ctrl+C। अगर सिग्नल हैंडल नहीं किया गया, तो कनेक्शन लटक जाएगा, और कनेक्शन पूल खाली नहीं होगा। कुछ ऐसे रीस्टार्ट के बाद डेटाबेस कहेगा: "माफ़ करें, और नहीं हो सकता"।

\n\n
package main\n\nimport (\n    "fmt"\n    "os"\n    "os/signal"\n    "syscall"\n)\n\nfunc main() {\n    // ... आपका कोड\n    fmt.Println("बंद करने के लिए Ctrl+C दबाएं")\n    // सिग्नल की प्रतीक्षा करें\n    sig := make(chan os.Signal, 1)\n    signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)\n    <-sig\n    fmt.Println("सिग्नल मिला, बंद हो रहे हैं...")\n}
\n\n

यह कोड सिग्नल को पकड़ लेता है, लेकिन अगर हमें जटिल क्लीनअप करना हो तो क्या होगा? यहीं से जादू शुरू होता है।

\n\n

अंदरूनी काम: signal.Notify और चैनल

\n\n

signal.Notify फंक्शन आपके चैनल को विशिष्ट सिग्नल प्राप्त करने के लिए रजिस्टर करता है। जब OS सिग्नल भेजता है, Go runtime उसे चैनल में डाल देता है। अगर चैनल बफ़र किया गया है (हमने बफ़र 1 बनाया है), तो सिग्नल खोएगा नहीं, भले ही प्राप्त करने वाली goroutine व्यस्त हो।

\n\n

महत्वपूर्ण बात: signal.Notify सिग्नल को रोकता नहीं है, बल्कि उसे फैलने से नहीं रोकता। अगर आप चैनल में सिग्नल को प्रोसेस नहीं करते, तो प्रोग्राम फिर भी defer (अगर main में हैं) के बाद बंद हो जाएगा। लेकिन अगर आप क्लीनअप करना चाहते हैं, तो उसके पूरा होने का इंतज़ार करना होगा।

\n\n

काम करने का तरीका:

\n
    \n
  • OS → प्रक्रिया को SIGINT भेजता है
  • \n
  • Go runtime → signal.Notify के ज़रिए रजिस्टर किए गए चैनल में सिग्नल डालता है
  • \n
  • आपकी goroutine → चैनल से पढ़ती है और क्लीनअप शुरू करती है
  • \n
  • क्लीनअप के बाद → os.Exit(0) कॉल करें या बस main से बाहर निकलें
  • \n
\n\n

लेकिन क्लीनअप को "defer स्टाइल" में कैसे करें? आखिर defer फंक्शन से लौटने पर चलता है, सिग्नल मिलने पर नहीं। जवाब: defer + main को सिग्नल मिलने तक ब्लॉक करना।

\n\n

रेसिपी: Ctrl+C पर "defer-जैसी" क्लीनअप

\n\n

विचार सरल है: हम क्लीनअप फंक्शन लिखते हैं जो बंद होने पर चलने चाहिए, और उन्हें defer पर "लटका" देते हैं। फिर main को सिग्नल मिलने तक ब्लॉक करते हैं। जब सिग्नल आता है, main बंद होता है, और सभी defer चलते हैं। सुंदर, है ना?

\n\n
package main\n\nimport (\n    "fmt"\n    "os"\n    "os/signal"\n    "syscall"\n    "time"\n)\n\nfunc cleanup() {\n    fmt.Println("क्लीनअप: कनेक्शन बंद किए जा रहे हैं...")\n    time.Sleep(2 * time.Second) // काम का अनुकरण\n    fmt.Println("क्लीनअप पूरा हुआ")\n}\n\nfunc main() {\n    // defer main के किसी भी तरीके से बंद होने पर चलेगा\n    defer cleanup()\n\n    // सिग्नल के लिए चैनल\n    sig := make(chan os.Signal, 1)\n    signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)\n\n    fmt.Println("सर्वर शुरू। रोकने के लिए Ctrl+C दबाएं।")\n    fmt.Println("क्लीनअप चल रहा है...")\n\n    //                

ब्लॉग

पुस्तक अनुशंसाएँ