paint-brush
Go में sync.Cond को समझना: शुरुआती लोगों के लिए एक गाइडद्वारा@ivanlemeshev
7,435 रीडिंग
7,435 रीडिंग

Go में sync.Cond को समझना: शुरुआती लोगों के लिए एक गाइड

द्वारा Ivan Lemeshev9m2024/04/28
Read on Terminal Reader

बहुत लंबा; पढ़ने के लिए

निष्कर्ष में, sync.Cond गो प्रोग्रामिंग भाषा में एक उपयोगी प्रकार है जो विशिष्ट स्थितियों के आधार पर गोरूटीन के बीच समन्वय और समन्वय की अनुमति देता है। यह स्थिति चर बनाने और प्रबंधित करने का एक तरीका प्रदान करता है। इसमें स्थितियों की प्रतीक्षा करने, संकेत देने और प्रसारण करने के तरीके हैं। `sync.Cond` का उपयोग करके, आप गो में अधिक नियंत्रित और सिंक्रनाइज़ समवर्ती कार्यक्रम लिख सकते हैं। यह ध्यान रखना महत्वपूर्ण है कि sync.Cond गो मानक लाइब्रेरी द्वारा प्रदान किए गए सिंक्रनाइज़ेशन प्राइमेटिव में से एक है, और इसका उपयोग आपके समवर्ती कार्यक्रम की विशिष्ट आवश्यकताओं पर निर्भर करता है। कुछ मामलों में, चैनल या sync.WaitGroup जैसे अन्य सिंक्रनाइज़ेशन प्राइमेटिव अधिक उपयुक्त हो सकते हैं।
featured image - Go में sync.Cond को समझना: शुरुआती लोगों के लिए एक गाइड
Ivan Lemeshev HackerNoon profile picture
0-item

परिचय

मैं sync.Cond प्रकार और उपयोग के मामलों तथा इसका उपयोग कब करना है, इस पर चर्चा करना चाहता हूँ।

sync.Cond क्या है?

गो प्रोग्रामिंग भाषा में, sync.Cond एक प्रकार है जिसे sync पैकेज में परिभाषित किया गया है जो एक कंडीशन वैरिएबल का प्रतिनिधित्व करता है। कंडीशन वैरिएबल सिंक्रोनाइज़ेशन प्राइमिटिव हैं जिनका उपयोग गोरूटीन को समन्वयित करने के लिए किया जाता है, ताकि उन्हें आगे बढ़ने से पहले एक विशिष्ट कंडीशन के सत्य होने की प्रतीक्षा करने की अनुमति मिल सके।


sync.Cond प्रकार कंडीशन वैरिएबल बनाने और प्रबंधित करने का एक तरीका प्रदान करता है। इसके तीन मुख्य तरीके हैं:


  1. Wait() : यह विधि कॉलिंग गोरआउटिन को तब तक प्रतीक्षा करने के लिए बाध्य करती है जब तक कि कोई दूसरा गोरआउटिन कंडीशन वैरिएबल को सिग्नल न दे। जब गोरआउटिन Wait() कॉल करता है, तो यह संबंधित लॉक को रिलीज़ करता है और तब तक निष्पादन को निलंबित करता है जब तक कि कोई दूसरा गोरआउटिन उसी sync.Cond वैरिएबल पर Signal() या Broadcast() कॉल न कर दे।


  2. Signal() : यह विधि कंडीशन वैरिएबल पर प्रतीक्षा कर रहे एक गोरूटीन को जगाती है। यदि कई गोरूटीन प्रतीक्षा कर रहे हैं, तो उनमें से केवल एक को जगाया जाता है। कौन सा गोरूटीन जागृत होगा इसका चुनाव मनमाना है और इसकी गारंटी नहीं है।


  3. Broadcast() : यह विधि कंडीशन वेरिएबल के लिए प्रतीक्षा कर रहे सभी गोरूटीन को जगाती है। जब Broadcast() को कॉल किया जाता है, तो सभी प्रतीक्षा कर रहे गोरूटीन जाग जाते हैं और आगे बढ़ सकते हैं।


ध्यान दें कि sync.Cond कंडीशन वेरिएबल तक पहुंच को सिंक्रनाइज़ करने के लिए एक संबद्ध sync.Mutex की आवश्यकता होती है।


sync.Cond का उपयोग करके, आप विशिष्ट स्थितियों के आधार पर गोरूटीन के निष्पादन को समन्वित कर सकते हैं, जिससे गो में अधिक नियंत्रित और सिंक्रनाइज़ समवर्ती प्रोग्रामिंग की अनुमति मिलती है।

सामान्य उपयोग के मामले

sync.Cond उपयोग आम तौर पर उन परिदृश्यों में किया जाता है जहाँ गोरूटीन को विशिष्ट स्थितियों के आधार पर एक दूसरे के साथ समन्वय और संचार करने की आवश्यकता होती है। आइए sync.Cond के सामान्य उपयोग के मामलों पर विचार करें।

गोरूटीन सिंक्रोनाइजेशन

sync.Cond उपयोग कई गोरूटीन के निष्पादन को सिंक्रनाइज़ करने के लिए किया जा सकता है। उदाहरण के लिए, आपके पास कई गोरूटीन हो सकते हैं जिन्हें आगे बढ़ने से पहले किसी विशिष्ट शर्त के संतुष्ट होने का इंतज़ार करना होगा। प्रतीक्षारत गोरूटीन cond.Wait() को कॉल कर सकते हैं, और सिग्नलिंग गोरूटीन cond.Signal() या cond.Broadcast() को कॉल कर सकते हैं ताकि शर्त पूरी होने पर प्रतीक्षारत गोरूटीन को जगाया जा सके।


 package main import ( "fmt" "sync" "time" ) func main() { var wg sync.WaitGroup var mu sync.Mutex cond := sync.NewCond(&mu) wg.Add(2) go func() { fmt.Println("Goroutine 1 is started") defer wg.Done() cond.L.Lock() defer cond.L.Unlock() fmt.Println("Goroutine 1 is waiting for condition") cond.Wait() fmt.Println("Goroutine 1 met the condition") fmt.Println("Goroutine 1 is done") }() go func() { fmt.Println("Goroutine 2 is started") defer wg.Done() time.Sleep(5 * time.Second) // Simulating some work cond.L.Lock() defer cond.L.Unlock() fmt.Println("Goroutine 2 is signaling condition") cond.Signal() fmt.Println("Goroutine 2 completed signaling") fmt.Println("Goroutine 2 is done") }() wg.Wait() }


इस उदाहरण में, हमारे पास दो गोरूटीन हैं। पहला गोरूटीन cond.Wait() उपयोग करके किसी शर्त का इंतज़ार करता है, जबकि दूसरा गोरूटीन cond.Signal() उपयोग करके शर्त का संकेत देता है।


जब प्रोग्राम निष्पादित होता है, तो पहला गोरूटीन लॉक प्राप्त करता है और फिर cond.Wait() को कॉल करता है। चूंकि शर्त अभी तक पूरी नहीं हुई है, इसलिए पहला गोरूटीन लॉक को रिलीज़ करता है और इसके निष्पादन को निलंबित कर देता है।


इस बीच, दूसरा गोरूटीन पाँच सेकंड के लिए सो जाता है, कुछ काम का अनुकरण करता है। यह लॉक प्राप्त करता है और फिर cond.Signal() को कॉल करता है। यह प्रतीक्षारत गोरूटीन को जगाता है, जो फिर लॉक प्राप्त करता है और निष्पादित करता है।


sync.Cond का उपयोग यह सुनिश्चित करता है कि पहला गोरूटीन तब तक प्रतीक्षा करता है जब तक कि दूसरा गोरूटीन स्थिति का संकेत नहीं देता, जिससे दो गोरूटीनों के बीच समन्वय और समन्वय संभव हो जाता है।

उत्पादक-उपभोक्ता समस्या

sync.Cond उत्पादक-उपभोक्ता समस्या को हल करने में उपयोगी हो सकता है, एक क्लासिक सिंक्रोनाइज़ेशन समस्या जिसमें दो प्रकार की प्रक्रियाएँ, उत्पादक और उपभोक्ता शामिल हैं, जो एक सामान्य निश्चित आकार के बफर या कतार को साझा करते हैं। जब नया डेटा उपभोग के लिए उपलब्ध होता है, तो उत्पादक गोरूटीन उपभोक्ता गोरूटीन को सूचित करने के लिए cond.Signal() या cond.Broadcast() का उपयोग कर सकते हैं।


 package main import ( "fmt" "sync" "time" ) const MaxMessageChannelSize = 5 func main() { var wg sync.WaitGroup var mu sync.Mutex cond := sync.NewCond(&mu) messageChannel := NewMessageChannel(MaxMessageChannelSize) producer := NewProducer(cond, messageChannel) consumer := NewConsumer(cond, messageChannel) wg.Add(2) go func() { defer wg.Done() for i := range 10 { producer.Produce(fmt.Sprintf("Message %d", i)) } }() go func() { defer wg.Done() for range 10 { consumer.Consume() } }() wg.Wait() } type MessageChannel struct { maxBufferSize int buffer []string } func NewMessageChannel(size int) *MessageChannel { return &MessageChannel{ maxBufferSize: size, buffer: make([]string, 0, size), } } func (mc *MessageChannel) IsEmpty() bool { return len(mc.buffer) == 0 } func (mc *MessageChannel) IsFull() bool { return len(mc.buffer) == mc.maxBufferSize } func (mc *MessageChannel) Add(message string) { mc.buffer = append(mc.buffer, message) } func (mc *MessageChannel) Get() string { message := mc.buffer[0] mc.buffer = mc.buffer[1:] return message } type Producer struct { cond *sync.Cond messageChannel *MessageChannel } func NewProducer(cond *sync.Cond, messageChannel *MessageChannel) *Producer { return &Producer{ cond: cond, messageChannel: messageChannel, } } func (p *Producer) Produce(message string) { time.Sleep(500 * time.Millisecond) // Simulating some work p.cond.L.Lock() defer p.cond.L.Unlock() for p.messageChannel.IsFull() { fmt.Println("Producer is waiting because the message channel is full") p.cond.Wait() } p.messageChannel.Add(message) fmt.Println("Producer produced the message:", message) p.cond.Signal() } type Consumer struct { id int cond *sync.Cond messageChannel *MessageChannel } func NewConsumer(cond *sync.Cond, messageChannel *MessageChannel) *Consumer { return &Consumer{ cond: cond, messageChannel: messageChannel, } } func (c *Consumer) Consume() { time.Sleep(1 * time.Second) // Simulating some work c.cond.L.Lock() defer c.cond.L.Unlock() for c.messageChannel.IsEmpty() { fmt.Println("Consumer is waiting because the message channel is empty") c.cond.Wait() } message := c.messageChannel.Get() fmt.Println("Consumer consumed the message:", message) c.cond.Signal() }


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


निर्माता गोरूटीन संदेश चैनल में संदेश जोड़ता है और नया डेटा उपलब्ध होने पर उपभोक्ता गोरूटीन को सूचित करने के लिए cond.Signal() का उपयोग करता है। यदि संदेश चैनल भरा हुआ है, तो निर्माता गोरूटीन cond.Wait() का उपयोग करके तब तक प्रतीक्षा करता है जब तक कि उपभोक्ता कुछ डेटा का उपभोग नहीं कर लेता और संदेश चैनल में स्थान खाली नहीं कर देता।


इसी तरह, उपभोक्ता गोरूटीन संदेश चैनल से संदेशों का उपभोग करता है और संदेश चैनल में स्थान उपलब्ध होने पर निर्माता गोरूटीन को सूचित करने के लिए cond.Signal() का उपयोग करता है। यदि यह खाली है, तो उपभोक्ता गोरूटीन cond.Wait() का उपयोग करके तब तक प्रतीक्षा करता है जब तक कि निर्माता कुछ डेटा तैयार करके उसे संदेश चैनल में नहीं जोड़ देता।


यहाँ, sync.Cond निर्माता और उपभोक्ता गोरूटीन के बीच समन्वय और तुल्यकालन की अनुमति देता है। यह सुनिश्चित करता है कि जब संदेश चैनल खाली हो तो उपभोक्ता प्रतीक्षा करे, और जब यह भर जाए तो निर्माता प्रतीक्षा करे, जिससे निर्माता-उपभोक्ता समस्या हल हो जाती है।

संसाधन सिंक्रनाइज़ेशन

मान लीजिए कि कई गोरूटीन को किसी साझा संसाधन तक विशेष पहुँच की आवश्यकता है। पहुँच को समन्वित करने के लिए sync.Cond उपयोग किया जा सकता है। उदाहरण के लिए, वर्कर गोरूटीन के एक पूल को तब तक प्रतीक्षा करनी पड़ सकती है जब तक कि संसाधन की एक निश्चित संख्या उपलब्ध न हो जाए, उसके बाद ही वे प्रसंस्करण शुरू कर सकते हैं। गोरूटीन cond.Wait() का उपयोग करके कंडीशन वैरिएबल पर प्रतीक्षा कर सकते हैं, और cond.Signal() या cond.Broadcast() का उपयोग करके संसाधन जारी करने के बारे में सूचित कर सकते हैं।


 package main import ( "fmt" "sync" "time" ) const MaxResources = 3 func main() { var wg sync.WaitGroup var mu sync.Mutex cond := sync.NewCond(&mu) resourceProvider := NewResourceProvider(cond, MaxResources) wg.Add(10) for i := range 10 { go func(workerID int) { defer wg.Done() worker := NewWorker(workerID, cond, resourceProvider) worker.Run() }(i) } wg.Wait() } type ResourceProvider struct { maxResources int availableResources int cond *sync.Cond } func NewResourceProvider(cond *sync.Cond, maxResources int) *ResourceProvider { return &ResourceProvider{ cond: cond, availableResources: maxResources, } } func (rp *ResourceProvider) AvailableResources() int { return rp.availableResources } func (rp *ResourceProvider) AcquireResoirce() { rp.availableResources-- } func (rp *ResourceProvider) ReleaseResource() { rp.availableResources++ } type Worker struct { id int cond *sync.Cond rp *ResourceProvider } func NewWorker(workerID int, cond *sync.Cond, rp *ResourceProvider) *Worker { return &Worker{ id: workerID, cond: cond, rp: rp, } } func (w *Worker) Run() { w.cond.L.Lock() for w.rp.AvailableResources() == 0 { fmt.Printf("Worker %d is waiting for resources\n", w.id) w.cond.Wait() } w.rp.AcquireResoirce() fmt.Printf("Worker %d acquired resource. Remaining resources: %d\n", w.id, w.rp.AvailableResources()) w.cond.L.Unlock() time.Sleep(1 * time.Second) // Simulating work w.cond.L.Lock() defer w.cond.L.Unlock() w.rp.ReleaseResource() fmt.Printf("Worker %d released resource. Remaining resources: %d\n", w.id, w.rp.AvailableResources()) w.cond.Signal() }


इस उदाहरण में, हमारे पास कई वर्कर गोरूटीन हैं जिन्हें सीमित संसाधनों तक विशेष पहुँच की आवश्यकता है। वर्कर गोरूटीन अन्य वर्करों के साथ समन्वय करने के लिए cond.Signal() का उपयोग करके संसाधन प्राप्त करते हैं और रिलीज़ करते हैं। यदि कोई संसाधन उपलब्ध नहीं है, तो वर्कर गोरूटीन using cond.Wait() प्रतीक्षा करते हैं जब तक कि अन्य गोरूटीन संसाधन रिलीज़ नहीं कर देते।


इस उदाहरण में, sync.Cond कार्यकर्ता गोरूटीन के बीच समन्वय और समन्वय की अनुमति देता है, यह सुनिश्चित करता है कि जब कोई संसाधन उपलब्ध न हो तो कार्यकर्ता गोरूटीन प्रतीक्षा करें, जिससे संसाधन पहुंच को प्रभावी ढंग से सिंक्रनाइज़ किया जा सके।

घटना अधिसूचना

sync.Cond उपयोग सिस्टम में विशिष्ट घटनाओं या परिवर्तनों के बारे में गोरूटीन को सूचित करने के लिए किया जा सकता है। उदाहरण के लिए, आप किसी विशिष्ट घटना के लिए प्रतीक्षारत गोरूटीन रख सकते हैं। जब घटना होती है, तो सिग्नलिंग गोरूटीन cond.Signal() या cond.Broadcast() का उपयोग करके प्रतीक्षारत गोरूटीन को जगा सकता है और उन्हें घटना को संभालने की अनुमति दे सकता है।


 package main import ( "fmt" "sync" "time" ) const maxWorkersCount = 10 func main() { var counter int32 var wg sync.WaitGroup var mu sync.Mutex cond := sync.NewCond(&mu) wg.Add(maxWorkersCount) for i := range maxWorkersCount { go func(workerID int) { defer wg.Done() fmt.Printf("Worker %d performing work\n", workerID) time.Sleep(1 * time.Second) // Simulate work cond.L.Lock() defer cond.L.Unlock() counter++ if counter == maxWorkersCount { fmt.Println("All workers have reached the barrier") cond.Broadcast() } else { fmt.Printf("Worker %d is waiting at the barrier\n", workerID) cond.Wait() } fmt.Printf("Worker %d passed the barrier\n", workerID) }(i) } wg.Wait() }


यहाँ, हमारे पास कई वर्कर गोरूटीन हैं जो काम करते हैं और बैरियर पॉइंट पर सिंक्रोनाइज़ करते हैं। वर्कर गोरूटीन काउंटर को बढ़ाते हैं और फिर या तो बैरियर पर प्रतीक्षा करते हैं या बैरियर तक पहुँचने वाले वर्कर्स की गिनती के आधार पर cond.Wait() और cond.Broadcast() उपयोग करके बैरियर को संकेत देते हैं।


प्रत्येक वर्कर गोरूटीन कुछ काम करता है और फिर काउंटर वैरिएबल को बढ़ाने के लिए लॉक प्राप्त करता है। यदि वर्तमान वर्कर बैरियर तक पहुँचने वाला अंतिम व्यक्ति है, तो यह सभी प्रतीक्षारत वर्करों को जगाने के लिए cond.Broadcast() का उपयोग करके बैरियर की स्थिति को प्रसारित करता है। अन्यथा, यह अंतिम वर्कर द्वारा अधिसूचित किए जाने के लिए cond.Wait() का उपयोग करके बैरियर पर प्रतीक्षा करता है।


बैरियर सिंक्रोनाइजेशन यह सुनिश्चित करता है कि सभी वर्कर गोरूटीन बैरियर तक पहुंचें, इससे पहले कि उनमें से कोई भी उससे आगे बढ़े। यह उन परिदृश्यों में उपयोगी हो सकता है, जिनमें वर्कफ़्लो में किसी विशिष्ट बिंदु पर कई गोरूटीन के निष्पादन को सिंक्रोनाइज़ करने की आवश्यकता होती है।


ध्यान दें कि इस उदाहरण में बैरियर को एक सरल काउंटर का उपयोग करके लागू किया गया है। हालाँकि, अधिक जटिल परिदृश्यों में, आपको सही सिंक्रोनाइज़ेशन सुनिश्चित करने और रेस स्थितियों से बचने के लिए अतिरिक्त सिंक्रोनाइज़ेशन तंत्र या शर्तों पर विचार करने की आवश्यकता हो सकती है।

निष्कर्ष

निष्कर्ष में, sync.Cond गो प्रोग्रामिंग भाषा में एक उपयोगी प्रकार है जो विशिष्ट स्थितियों के आधार पर गोरूटीन के बीच समन्वय और समन्वय की अनुमति देता है। यह स्थिति चर बनाने और प्रबंधित करने का एक तरीका प्रदान करता है। इसमें स्थितियों की प्रतीक्षा करने, संकेत देने और प्रसारित करने के तरीके हैं। `sync.Cond` का उपयोग करके, आप गो में अधिक नियंत्रित और सिंक्रनाइज़ समवर्ती प्रोग्राम लिख सकते हैं।


यह ध्यान रखना महत्वपूर्ण है कि sync.Cond गो मानक लाइब्रेरी द्वारा प्रदान किए गए सिंक्रोनाइज़ेशन प्राइमेटिव में से एक है, और इसका उपयोग आपके समवर्ती कार्यक्रम की विशिष्ट आवश्यकताओं पर निर्भर करता है। कुछ मामलों में, चैनल या sync.WaitGroup जैसे अन्य सिंक्रोनाइज़ेशन प्राइमेटिव अधिक उपयुक्त हो सकते हैं।