Go ने समवर्तीता में सबको पीछे क्यों छोड़ा?
\n\nएक शेफ की कल्पना करें जो एक साथ दस व्यंजन बना रहा है। वह पागलों की तरह एक चूल्हे से दूसरे चूल्हे तक नहीं भागता - वह बस कार्यों के बीच कुशलता से स्विच करता है: जब मांस पक रहा होता है, तो सलाद काटता है; जब पानी उबल रहा होता है, तो प्लेट सजाता है। प्रोग्रामिंग की दुनिया में इसे समवर्तीता कहा जाता है - प्रत्येक कार्य के पूरा होने की प्रतीक्षा किए बिना कई कार्यों को प्रबंधित करने की क्षमता।
\n\nGo भाषा (Golang) का जन्म समवर्तीता की समस्याओं को हल करने के लिए ही हुआ था। Google के इसके निर्माता C++ और Java में मल्टीथ्रेडिंग की जटिलताओं से थक गए थे। उन्होंने गोरूटीन बनाए - हल्के-फुल्के थ्रेड जो कोड की एक पंक्ति से शुरू होते हैं। इस लेख में, हम समझेंगे कि Go में समवर्तीता कैसे काम करती है, इसकी तुलना OS थ्रेड्स और Python के async/await प्रतिमान से करेंगे, और सबसे महत्वपूर्ण बात, वास्तविक कोड उदाहरण दिखाएंगे जिन्हें आप आज ही लागू कर सकते हैं।
\n\nगोरूटीन बनाम OS थ्रेड्स: हल्की घुड़सवार सेना की लड़ाई
\n\nजब कोई डेवलपर 'मल्टीथ्रेडिंग' सुनता है, तो वह आमतौर पर ऑपरेटिंग सिस्टम के थ्रेड्स के बारे में सोचता है। लेकिन Go में सब कुछ अलग है। आइए मुख्य अंतरों को समझें।
\n\nOS थ्रेड क्या है?
\nथ्रेड एक भारी वस्तु है जिसे OS कर्नेल द्वारा प्रबंधित किया जाता है। प्रत्येक थ्रेड का अपना स्टैक (आमतौर पर 1-8 MB) होता है, जिसे बनाने और स्विच करने के लिए सिस्टम कॉल की आवश्यकता होती है। 10,000 थ्रेड बनाने का मतलब है मेमोरी और CPU को मार डालना। Go में एक अलग दर्शन निहित है।
\n\nगोरूटीन - Go का हल्का थ्रेड
\nगोरूटीन एक फंक्शन है जो समवर्ती रूप से निष्पादित होता है। इसे कीवर्ड go से शुरू किया जाता है। मुख्य विशेषताएं:
- \n
- स्टैक का आकार: 2-4 KB से शुरू होता है और गतिशील रूप से बढ़ता/घटता है। \n
- निर्माण: हजारों गोरूटीन मिलीसेकंड में बनाए जाते हैं। \n
- शेड्यूलर: Go runtime स्वचालित रूप से गोरूटीन को OS थ्रेड्स पर वितरित करता है (आमतौर पर उनकी संख्या CPU कोर की संख्या के बराबर होती है)। \n
- सिंटैक्स: बस फंक्शन कॉल से पहले
goजोड़ें। \n
package main\n\nimport (\n "fmt"\n "time"\n)\n\nfunc sayHello(name string) {\n for i := 0; i < 3; i++ {\n fmt.Println("नमस्ते,", name)\n time.Sleep(100 * time.Millisecond)\n }\n}\n\nfunc main() {\n // दो गोरूटीन शुरू करें\n go sayHello("Go")\n go sayHello("Goroutine")\n\n // गोरूटीन को निष्पादित होने का समय दें\n time.Sleep(1 * time.Second)\n fmt.Println("मुख्य फंक्शन समाप्त")\n}\n\nपरिणाम: दो गोरूटीन के संदेश आपस में मिल जाएंगे। Go स्वचालित रूप से उनके बीच स्विच करता है, एक दूसरे को ब्लॉक किए बिना। इस कोड को चलाने का प्रयास करें - आप देखेंगे कि 'नमस्ते, Go' और 'नमस्ते, Goroutine' बारी-बारी से प्रिंट होते हैं।
\n\nचैनल: सिंक्रोनाइज़ेशन का मुख्य उपकरण
\n\nगोरूटीन बहुत अच्छे हैं, लेकिन वे आपस में कैसे संवाद करें? Go में डिफ़ॉल्ट रूप से साझा मेमोरी नहीं है। इसके बजाय, सिद्धांत का उपयोग किया जाता है: 'मेमोरी साझा करके संवाद न करें; संवाद करके मेमोरी साझा करें'। इसके लिए चैनल का उपयोग किया जाता है।
\n\nचैनल एक टाइप किया गया 'पाइपलाइन' है जिसके माध्यम से गोरूटीन डेटा भेजते और प्राप्त करते हैं। यह सुरक्षित, सरल और सुरुचिपूर्ण है।
\n\nउदाहरण: संख्याओं का समानांतर प्रसंस्करण
\npackage main\n\nimport "fmt"\n\nfunc squareWorker(numbers <-chan int, results chan<- int) {\n for n := range numbers {\n results <- n * n\n }\n close(results)\n}\n\nfunc main() {\n numbers := make(chan int, 5)\n results := make(chan int, 5)\n\n // वर्कर शुरू करें\n go squareWorker(numbers, results)\n\n // डेटा भेजें\n numbers <- 2\n numbers <- 4\n numbers <- 6\n close(numbers)\n\n // परिणाम पढ़ें\n for res := range results {\n fmt.Println("वर्ग:", res)\n }\n}\n\nइस उदाहरण में, हम बफ़र किए गए चैनल का उपयोग करते हैं (दूसरा तर्क बफ़र का आकार है)। गोरूटीन-वर्कर numbers चैनल से पढ़ता है, वर्ग करता है