Go में टास्क क्यू का परिचय
\nटास्क क्यू (task queues) हाई-लोड एप्लिकेशन डेवलपमेंट में एक महत्वपूर्ण पैटर्न है। यह डेटा को एसिंक्रोनस रूप से प्रोसेस करने, लोड को वितरित करने और सिस्टम की विश्वसनीयता बढ़ाने में मदद करता है। Go भाषा में, goroutines और channels जैसे बिल्ट-इन कंकरेंसी मैकेनिज्म की बदौलत, क्यू को लागू करना बेहद आसान और कुशल हो जाता है।
\nइस लेख में, हम Go में टास्क क्यू को शुरुआत से बनाना सीखेंगे। आप जानेंगे कि goroutines और channels क्या हैं, worker pool कैसे बनाएं, और टास्क को parallel रूप से कैसे प्रोसेस करें। यह सामग्री उन शुरुआती डेवलपर्स के लिए है जिन्हें Go सिंटैक्स की बुनियादी जानकारी है।
\n\nबुनियादी बातें: Goroutines और Channels
\nक्यू पर जाने से पहले, Go के दो मूलभूत कॉन्सेप्ट को समझना ज़रूरी है:
\n- \n
- Goroutine — एक हल्का थ्रेड। इसे फंक्शन कॉल से पहले
goकीवर्ड से शुरू किया जाता है। \n - Channel — goroutines के बीच डेटा ट्रांसफर करने के लिए एक टाइप किया गया "चैनल"। यह उनके काम को सिंक्रोनाइज़ करता है। \n
सबसे सरल goroutine और channel का उदाहरण:
\npackage main\n\nimport "fmt"\n\nfunc main() {\n ch := make(chan string)\n\n go func() {\n ch <- "Goroutine से नमस्ते!"\n }()\n\n msg := <-ch\n fmt.Println(msg)\n}\n\nइस उदाहरण में, हमने एक चैनल ch बनाया, एक अनाम goroutine शुरू की जो इसमें मैसेज भेजती है, और मुख्य goroutine ने यह मैसेज प्राप्त किया। टास्क क्यू इसी मैकेनिज्म पर आधारित होते हैं।
सरल टास्क क्यू का कार्यान्वयन
\nसबसे सरल टास्क क्यू एक चैनल है जिसमें हम टास्क डालते हैं, और एक या अधिक goroutines उन्हें प्रोसेस करते हैं। आइए एक बुनियादी उदाहरण देखें:
\npackage main\n\nimport (\n "fmt"\n "time"\n)\n\nfunc worker(id int, tasks <-chan int) {\n for task := range tasks {\n fmt.Printf("वर्कर %d ने टास्क %d शुरू किया\\n", id, task)\n time.Sleep(time.Second) // काम का अनुकरण\n fmt.Printf("वर्कर %d ने टास्क %d पूरा किया\\n", id, task)\n }\n}\n\nfunc main() {\n tasks := make(chan int, 10) // बफ़र किया गया चैनल\n\n // एक वर्कर शुरू करें\n go worker(1, tasks)\n\n // 5 टास्क भेजें\n for i := 1; i <= 5; i++ {\n tasks <- i\n }\n\n close(tasks) // चैनल बंद करें\n time.Sleep(2 * time.Second) // प्रोसेसिंग के लिए समय दें\n}\n\nयहाँ worker एक फंक्शन है जो tasks चैनल से पढ़ता है। चैनल बफ़र किया गया है (क्षमता 10), इसलिए हम बिना ब्लॉक किए कई टास्क भेज सकते हैं। चैनल बंद होने के बाद, for range लूप अपने आप खत्म हो जाएगा।
ध्यान दें: हमने <-chan int का उपयोग किया है — यह केवल पढ़ने के लिए चैनल है। यह एक अच्छा अभ्यास है जो स्पष्ट रूप से बताता है कि वर्कर इस चैनल में डेटा नहीं भेजेगा।
बफ़र किए गए और बिना बफ़र वाले चैनल
\nउपरोक्त उदाहरण में हमने बफ़र किया गया चैनल (make(chan int, 10)) का उपयोग किया। दोनों प्रकार के चैनलों में अंतर:
- \n
- बिना बफ़र वाला चैनल — भेजना तब तक ब्लॉक होता है जब तक प्राप्तकर्ता डेटा न पढ़ ले। सिंक्रोनाइज़ेशन के लिए आदर्श। \n
- बफ़र किया गया चैनल — भेजना केवल तब ब्लॉक होता है जब बफ़र भर जाए। टास्क जमा करने और देरी कम करने में मदद करता है। \n
टास्क क्यू के लिए आमतौर पर बफ़र किए गए चैनल का उपयोग किया जाता है, ताकि प्रोड्यूसर टास्क भेजकर तुरंत आगे बढ़ सके, बिना इसके पूरा होने का इंतज़ार किए।
\n\nGo में वर्कर पूल (Worker Pool)
\nएक वर्कर अच्छा है, लेकिन कई वर्कर बेहतर हैं। वर्कर पूल आपको प्रोसेसर के सभी कोर का उपयोग करके टास्क को parallel रूप से प्रोसेस करने की अनुमति देता है। इसे लागू करने का तरीका यहाँ है:
\npackage main\n\nimport (\n "fmt"\n "sync"\n "time"\n)\n\nfunc worker(id int, tasks <-chan int, wg *sync.WaitGroup) {\n defer wg.Done()\n fo