Go में समवर्तीता और मल्टीथ्रेडिंग: A से Z तक
\n\nGo (Golang) को आधुनिक कंप्यूटिंग लोड को ध्यान में रखकर डिज़ाइन किया गया था। मल्टी-कोर प्रोसेसर और क्लाउड सेवाओं के युग में, समवर्तीता के साथ कुशलतापूर्वक काम करना बैकएंड डेवलपर के लिए एक अनिवार्य कौशल बन गया है। इस लेख में, हम देखेंगे कि Go समानांतर कोड निष्पादन की समस्याओं को कैसे हल करता है, गोरूटीन OS थ्रेड्स से कैसे भिन्न हैं, और चैनल और म्यूटेक्स का सही उपयोग कैसे करें।
\n\n1. गोरूटीन: हल्के थ्रेड्स
\n\nगोरूटीन (goroutine) एक फ़ंक्शन या मेथड है जो अन्य गोरूटीन के साथ समवर्ती रूप से निष्पादित होता है। ऑपरेटिंग सिस्टम थ्रेड्स के विपरीत, गोरूटीन में न्यूनतम ओवरहेड होता है: उनका स्टैक 2-4 KB से शुरू होता है और गतिशील रूप से बढ़ सकता है। गोरूटीन शुरू करना अविश्वसनीय रूप से आसान है - बस फ़ंक्शन कॉल से पहले कीवर्ड go जोड़ें।
package main\n\nimport (\n "fmt"\n "time"\n)\n\nfunc say(text string) {\n for i := 0; i < 3; i++ {\n fmt.Println(text)\n time.Sleep(100 * time.Millisecond)\n }\n}\n\nfunc main() {\n go say("Hello from goroutine")\n say("Hello from main")\n // गोरूटीन को पूरा होने का समय दें\n time.Sleep(500 * time.Millisecond)\n}\n\nध्यान दें: प्रोग्राम main() फ़ंक्शन के निष्पादन के तुरंत बाद समाप्त हो जाता है, शुरू की गई गोरूटीन के पूरा होने की प्रतीक्षा किए बिना। सिंक्रोनाइज़ेशन के लिए हम time.Sleep का उपयोग करते हैं (वास्तविक प्रोजेक्ट्स में - sync.WaitGroup या चैनल)।
2. चैनल: सुरक्षित डेटा ट्रांसफर
\n\nचैनल (channels) टाइप किए गए "पाइप" हैं जिनके माध्यम से गोरूटीन डेटा का आदान-प्रदान करते हैं। वे दो मुख्य कार्यों को हल करते हैं: निष्पादन का सिंक्रोनाइज़ेशन और मानों का स्थानांतरण। चैनल बफ़र किए गए और बिना बफ़र वाले हो सकते हैं।
\n\nबिना बफ़र वाला चैनल
\nभेजने का ऑपरेशन गोरूटीन को तब तक ब्लॉक करता है जब तक कोई दूसरी गोरूटीन रीड नहीं कर लेती। यह "ऑन-द-फ्लाई" सिंक्रोनाइज़ेशन सुनिश्चित करता है।
\n\npackage main\n\nimport "fmt"\n\nfunc main() {\n ch := make(chan string)\n\n go func() {\n ch <- "गोरूटीन से नमस्ते!"\n }()\n\n msg := <-ch\n fmt.Println(msg)\n}\n\nबफ़र किया गया चैनल
\nबफ़र वाला चैनल बिना तुरंत पढ़े कई मान भेजने की अनुमति देता है, जब तक बफ़र भर न जाए।
\n\npackage main\n\nimport "fmt"\n\nfunc main() {\n ch := make(chan int, 2)\n ch <- 10\n ch <- 20\n // ch <- 30 // deadlock! बफ़र भर गया\n\n fmt.Println(<-ch) // 10\n fmt.Println(<-ch) // 20\n}\n\nRange और close
\nचैनल को बंद करना प्राप्तकर्ताओं को संकेत देता है कि और डेटा नहीं आएगा। for range लूप चैनल बंद होने पर स्वचालित रूप से समाप्त हो जाता है।
package main\n\nimport "fmt"\n\nfunc producer(ch chan int) {\n for i := 1; i <= 5; i++ {\n ch <- i\n }\n close(ch)\n}\n\nfunc main() {\n ch := make(chan int)\n go producer(ch)\n\n for val := range ch {\n fmt.Println("प्राप्त:", val)\n }\n}\n\n3. सिंक्रोनाइज़ेशन और म्यूटेक्स
\n\nचैनल की शक्ति के बावजूद, कभी-कभी साझा डेटा तक सीधी पहुँच की आवश्यकता होती है। sync पैकेज क्रिटिकल सेक्शन की सुरक्षा के लिए म्यूटेक्स (sync.Mutex) और रीडर-म्यूटेक्स (sync.RWMutex) प्रदान करता है।
package main\n\nimport (\n "fmt"\n "sync"\n)\n\nvar counter = 0\nvar mu sync.Mutex\n\nfunc increment(wg *sync.WaitGroup) {\n defer wg.Done()\n mu.Lock()\n counter++\n mu.Unlock()\n}\n\nfunc main() {\n var wg sync.WaitGroup\n for i := 0; i < 1000; i++ {\n wg.Add(1)\n go increment(&wg)\n }\n wg.Wait()\n fmt.Println("अंतिम काउंटर:", counter)\n}\n\nयहाँ sync.WaitGroup सभी गोरूटीन के पूरा होने की प्रतीक्षा करता है, और म्यूटेक्स डेटा रेस (data race) को रोकता है। म्यूटेक्स के बिना, परिणाम अप्रत्याशित होगा।