2016-04-26 10 views
10

मुझे sync.Cond का सही तरीके से उपयोग करने का तरीका पता लगाने में परेशानी हो रही है। जो मैं बता सकता हूं, लॉकर लॉक करने और हालत की प्रतीक्षा विधि का आविष्कार करने के बीच दौड़ की स्थिति मौजूद है।sync.Cond का सही तरीके से उपयोग कैसे करें?

package main 

import (
    "sync" 
    "time" 
) 

func main() { 
    m := sync.Mutex{} 
    c := sync.NewCond(&m) 
    go func() { 
     time.Sleep(1 * time.Second) 
     c.Broadcast() 
    }() 
    m.Lock() 
    time.Sleep(2 * time.Second) 
    c.Wait() 
} 

[Run on the Go Playground]

यह एक तत्काल आतंक का कारण बनता है: इस उदाहरण रेस स्थिति अनुकरण करने के लिए मुख्य goroutine में दो लाइनों के बीच एक कृत्रिम देरी कहते हैं

fatal error: all goroutines are asleep - deadlock! 

goroutine 1 [semacquire]: 
sync.runtime_Syncsemacquire(0x10330208, 0x1) 
    /usr/local/go/src/runtime/sema.go:241 +0x2e0 
sync.(*Cond).Wait(0x10330200, 0x0) 
    /usr/local/go/src/sync/cond.go:63 +0xe0 
main.main() 
    /tmp/sandbox301865429/main.go:17 +0x1a0

क्या क्या मैं गलत कर रहा हूँ? मैं इस स्पष्ट दौड़ की स्थिति से कैसे बचूं? क्या कोई बेहतर सिंक्रनाइज़ेशन निर्माण है जिसका उपयोग करना चाहिए?


संपादित करें: मुझे पता है मैं बेहतर होना चाहिए समस्या मैं यहाँ हल करने के लिए कोशिश कर रहा हूँ की व्याख्या की। मेरे पास एक लंबी दौड़ वाली गोरौटाइन है जो एक बड़ी फ़ाइल और कई अन्य गोरोटाइन डाउनलोड करती है जिन्हें उपलब्ध होने पर HTTP हेडर तक पहुंच की आवश्यकता होती है। यह समस्या यह लगता है की तुलना में कठिन है।

मैं चैनलों का उपयोग नहीं कर सकता क्योंकि केवल एक goroutine मूल्य प्राप्त होगा। और कुछ अन्य goroutines पहले से ही उपलब्ध होने के बाद हेडर को पुनर्प्राप्त करने की कोशिश कर रहे हैं।

डाउनलोडर goroutine बस HTTP हेडर को एक चर में संग्रहीत कर सकता है और उन तक पहुंच की सुरक्षा के लिए एक म्यूटेक्स का उपयोग कर सकता है। हालांकि, यह अन्य goroutines के लिए उपलब्ध होने के लिए "प्रतीक्षा" करने का एक तरीका प्रदान नहीं करता है।

मैंने सोचा था कि sync.Mutex और sync.Cond दोनों एक साथ इस लक्ष्य को पूरा कर सकते हैं लेकिन ऐसा लगता है कि यह संभव नहीं है।

उत्तर

1

मुझे अंत में ऐसा करने का एक तरीका पता चला और इसमें sync.Cond बिल्कुल शामिल नहीं है - बस म्यूटेक्स।

type Task struct { 
    m  sync.Mutex 
    headers http.Header 
} 

func NewTask() *Task { 
    t := &Task{} 
    t.m.Lock() 
    go func() { 
     defer t.m.Unlock() 
     // ...do stuff... 
    }() 
    return t 
} 

func (t *Task) WaitFor() http.Header { 
    t.m.Lock() 
    defer t.m.Unlock() 
    return t.headers 
} 

यह कैसे काम करता है?

म्यूटेक्स कार्य की शुरुआत में बंद कर दिया गया है, यह सुनिश्चित करना कि WaitFor() पर कॉल करने वाला कुछ भी ब्लॉक करेगा। एक बार शीर्षलेख उपलब्ध हो जाते हैं और goutout goroutine द्वारा अनलॉक हो जाने पर, प्रत्येक कॉल को WaitFor() पर एक बार निष्पादित किया जाएगा। सभी भविष्य की कॉल (गोरौटाइन समाप्त होने के बाद भी) में म्यूटेक्स को लॉक करने में कोई समस्या नहीं होगी, क्योंकि इसे हमेशा अनलॉक कर दिया जाएगा।

2
package main 

import (
    "fmt" 
    "sync" 
    "time" 
) 

func main() { 
    m := sync.Mutex{} 
    m.Lock() // main gouroutine is owner of lock 
    c := sync.NewCond(&m) 
    go func() { 
     m.Lock() // obtain a lock 
     defer m.Unlock() 
     fmt.Println("3. goroutine is owner of lock") 
     time.Sleep(2 * time.Second) // long computing - because you are the owner, you can change state variable(s) 
     c.Broadcast()    // State has been changed, publish it to waiting goroutines 
     fmt.Println("4. goroutine will release lock soon (deffered Unlock") 
    }() 
    fmt.Println("1. main goroutine is owner of lock") 
    time.Sleep(1 * time.Second) // initialization 
    fmt.Println("2. main goroutine is still lockek") 
    c.Wait() // Wait temporarily release a mutex during wating and give opportunity to other goroutines to change the state. 
    // Because you don't know, whether this is state, that you are waiting for, is usually called in loop. 
    m.Unlock() 
    fmt.Println("Done") 
} 

http://play.golang.org/p/fBBwoL7_pm

+0

क्या होगा अगर यह goroutine शुरू करने से पहले म्युटेक्स लॉक करने के लिए संभव नहीं है? उदाहरण के लिए, प्रतीक्षा() को कॉल करने वाले अन्य goroutines हो सकते हैं। –

+0

से भी संभव है, कि जब ब्रॉडकास्ट कहा जाता है, तो कोई अन्य गोरौटाइन अधिसूचित नहीं किया जाएगा। यह भी ठीक है - लेकिन हम दोनों का क्या उल्लेख नहीं है - आमतौर पर स्थिति कुछ राज्य से जुड़ी होती है। और प्रतीक्षा का मतलब है - मैं इस स्थिति में सिस्टम जारी रखने के दौरान जारी नहीं रख सकता, प्रतीक्षा करें। और प्रसारण का मतलब है - राज्य बदल गया है, हर कोई जो इंतजार कर रहा है उसे जांचना चाहिए कि वह जारी रख सकता है या नहीं। कृपया goroutines दोनों में गणना की गई सटीक रूप से वर्णन करें, और उन्हें एक-दूसरे से संवाद क्यों करना है। – lofcek

+0

क्षमा करें, मुझे मूल प्रश्न में अधिक जानकारी मिलनी चाहिए थी। मैंने एक संपादन जोड़ा है जो ठीक से वर्णन करता है कि मैं क्या करने की कोशिश कर रहा हूं। –

1

प्रसारण जायें जो कि अपने समय अंतराल के साथ होता है कभी नहीं के लिए c.Wait आप की तरह लग रहा।

time.Sleep(3 * time.Second) //Broadcast after any Wait for it 
c.Broadcast() 

अपने टुकड़ा के साथ http://play.golang.org/p/OE8aP4i6gY .या मैं कुछ है कि आप हासिल करने की कोशिश याद आ रही है काम करने के लिए लगता है?

5

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

यदि आपको प्रत्येक लेखन और पढ़ने के लिए एक गोरौटाइन है तो आपको वास्तव में sync.Cond की आवश्यकता नहीं है - एक sync.Mutex उनके बीच संवाद करने के लिए पर्याप्त होगा। sync.Cond उन स्थितियों में उपयोगी हो सकता है जहां एकाधिक पाठक साझा संसाधनों के लिए उपलब्ध होने की प्रतीक्षा करते हैं।

var sharedRsc = make(map[string]interface{}) 
func main() { 
    var wg sync.WaitGroup 
    wg.Add(2) 
    m := sync.Mutex{} 
    c := sync.NewCond(&m) 
    go func() { 
     // this go routine wait for changes to the sharedRsc 
     c.L.Lock() 
     for len(sharedRsc) == 0 { 
      c.Wait() 
     } 
     fmt.Println(sharedRsc["rsc1"]) 
     c.L.Unlock() 
     wg.Done() 
    }() 

    go func() { 
     // this go routine wait for changes to the sharedRsc 
     c.L.Lock() 
     for len(sharedRsc) == 0 { 
      c.Wait() 
     } 
     fmt.Println(sharedRsc["rsc2"]) 
     c.L.Unlock() 
     wg.Done() 
    }() 

    // this one writes changes to sharedRsc 
    c.L.Lock() 
    sharedRsc["rsc1"] = "foo" 
    sharedRsc["rsc2"] = "bar" 
    c.Broadcast() 
    c.L.Unlock() 
    wg.Wait() 
} 

Playground

कहा करने के बाद कि, चैनल का उपयोग अभी भी चारों ओर अगर स्थिति की अनुमति डेटा पास करने के लिए सिफारिश तरीका है।

नोट: sync.WaitGroup यहां केवल गोरोटाइन को उनके निष्पादन को पूरा करने के लिए प्रतीक्षा करने के लिए उपयोग किया जाता है।

3

आपको यह सुनिश्चित करने की ज़रूरत है कि के बाद आपकी कॉल c.ait पर कॉल करें। अपने कार्यक्रम के सही संस्करण होगा:

package main 

import (
    "fmt" 
    "sync" 
) 

func main() { 
    m := &sync.Mutex{} 
    c := sync.NewCond(m) 
    m.Lock() 
    go func() { 
     m.Lock() // Wait for c.Wait() 
     c.Broadcast() 
     m.Unlock() 
    }() 
    c.Wait() // Unlocks m 
} 

https://play.golang.org/p/O1r8v8yW6h

संबंधित मुद्दे