2013-08-28 8 views
8

से एक ही टुकड़ा में कैसे संलग्न करें मेरे समवर्ती goroutines है जो एक ही टुकड़ा करने के लिए एक (सूचक को एक) संरचना जोड़ना चाहते हैं। इसे आप कैसे लिखते हैं इसे समेकन-सुरक्षित बनाने के लिए?गोलांग समेकन: विभिन्न goroutines

यह मेरा संगामिति-असुरक्षित कोड होगा, एक प्रतीक्षा समूह का उपयोग:

var wg sync.WaitGroup 
MySlice = make([]*MyStruct) 
for _, param := range params { 
    wg.Add(1) 
    go func(param string) { 
     defer wg.Done() 
     OneOfMyStructs := getMyStruct(param) 
     MySlice = append(MySlice, &OneOfMyStructs) 
    }(param) 
} 
wg.Wait() 

मुझे लगता है कि आप संगामिति-सुरक्षा के लिए चैनलों जाना उपयोग करने के लिए की आवश्यकता होगी। क्या कोई उदाहरण के साथ योगदान कर सकता है?

+2

मेरा मानना ​​है कि प्रतिक्रिया यहाँ अच्छी तरह से है कि सवाल का जवाब: http://stackoverflow.com/questions/18467445/working-with-slices-of-structs-concurrently-using-references/18469210# 18469210 –

उत्तर

3

इससे निपटने का एक तरीका सबसे अच्छा तरीका है। यहां एक उदाहरण दिया गया है जिसे go playground पर चलाया जा सकता है।

package main 

import "fmt" 
import "sync" 
import "runtime" 

type T int 

func main() { 
    var slice []T 
    var wg sync.WaitGroup 

    queue := make(chan T, 1) 

    // Create our data and send it into the queue. 
    wg.Add(100) 
    for i := 0; i < 100; i++ { 
     go func(i int) { 
      defer wg.Done() 

      // Do stuff. 
      runtime.Gosched() 

      queue <- T(i) 
     }(i) 
    } 

    // Poll the queue for data and append it to the slice. 
    // Since this happens synchronously and in the same 
    // goroutine/thread, this can be considered safe. 
    go func() { 
     defer wg.Done() 
     for t := range queue { 
      slice = append(slice, t) 
     } 
    }() 

    // Wait for everything to finish. 
    wg.Wait() 

    fmt.Println(slice) 
} 

नोट: runtime.Gosched() कॉल नहीं है क्योंकि उन goroutines नियोजक को उपज नहीं है। जो डेडलॉक का कारण बनता है अगर हम स्पष्ट रूप से शेड्यूलर को ट्रिगर करने के लिए कुछ नहीं करते हैं। एक और विकल्प कुछ I/O (उदाहरण: stdout पर प्रिंट) करने के लिए किया जा सकता था। लेकिन मुझे अपने इरादे में आसान और स्पष्ट होने के लिए runtime.Gosched() मिल गया है।

+0

चैनल को गोरौटाइन प्राप्त करने के लिए डिफर्ज wg.Done() को कॉल करने की आवश्यकता क्यों है? –

+1

इसे स्थगित करने की आवश्यकता नहीं है। उस गोरौटाइन के अंत में बस एक 'wg.Done() 'कॉल इस मामले में काम करेगा। जब आपके पास एकाधिक निकास/रिटर्न होते हैं तो उचित व्यवहार सुनिश्चित करने के लिए अधिकतर उपयोगी होता है। – jimt

+4

दरअसल मेरा सवाल यह था कि दूसरी यात्रा में 'wg.Done()' को क्यों बुलाया जाना चाहिए? पहला लूप 100 के काउंटर को साफ़ करेगा। –

13

MySlice = append(MySlice, &OneOfMyStructs) को sync.Mutex के साथ गार्ड करने में कुछ भी गलत नहीं है। लेकिन निश्चित रूप से आप बफर आकार len(params) के साथ एक परिणाम चैनल प्राप्त कर सकते हैं सभी goroutines उनके उत्तरों भेजते हैं और एक बार आपका काम समाप्त हो जाने के बाद आप इस परिणाम चैनल से एकत्र करते हैं।

अपने params एक निश्चित आकार है:

MySlice = make([]*MyStruct, len(params)) 
for i, param := range params { 
    wg.Add(1) 
    go func(i int, param string) { 
     defer wg.Done() 
     OneOfMyStructs := getMyStruct(param) 
     MySlice[i] = &OneOfMyStructs 
    }(i, param) 
} 

सभी goroutines अलग स्मृति पर लिखते हैं जैसा कि इस सुरम्य नहीं है।

+2

यह आपके आखिरी विचारों में बहुत दिलचस्प है: यदि स्लाइस का आकार ज्ञात है और आप केवल ऑब्जेक्ट्स के पॉइंटर्स से निपट रहे हैं, तो आपको –

+0

पर एक समवर्ती तंत्र का उपयोग करने की आवश्यकता नहीं है, यह "पॉइंटर्स के टुकड़े" पर निर्भर नहीं है ": यह" MyStruct के टुकड़े "के लिए भी काम करेगा। फिर कोड एक ही स्मृति को कभी नहीं लिखता है। – Volker

+0

मुझे लगता है कि एक सूचक के लिए स्मृति आवंटन तय किया गया है, जबकि एक संरचना के लिए स्मृति आवंटन तय नहीं है। मुझे लगता है कि मैं गलत हूँ। –

5

@jimt द्वारा पोस्ट किया गया उत्तर बिल्कुल सही नहीं है, जिसमें यह चैनल में भेजे गए अंतिम मूल्य को याद करता है और अंतिम defer wg.Done() कभी नहीं कहा जाता है। नीचे स्निपेट में सुधार है।

https://play.golang.org/p/7N4sxD-Bai

package main 

import "fmt" 
import "sync" 

type T int 

func main() { 
    var slice []T 
    var wg sync.WaitGroup 

    queue := make(chan T, 1) 

    // Create our data and send it into the queue. 
    wg.Add(100) 
    for i := 0; i < 100; i++ { 
     go func(i int) { 
      // defer wg.Done() <- will result in the last int to be missed in the receiving channel 
      queue <- T(i) 
     }(i) 
    } 

    go func() { 
     // defer wg.Done() <- Never gets called since the 100 `Done()` calls are made above, resulting in the `Wait()` to continue on before this is executed 
     for t := range queue { 
      slice = append(slice, t) 
      wg.Done() // ** move the `Done()` call here 
     } 
    }() 

    wg.Wait() 

    // now prints off all 100 int values 
    fmt.Println(slice) 
} 
1

आप या तो एसिंक्रोनस रूप से आइटम इकट्ठा करने के लिए डेनियल के समाधान और एक WaitGroup, जैसे कि, अंतिम टुकड़ा आकार में जाना जाता है अगर, या आप use a channel उपयोग करना और फिर टुकड़ा गतिशील हो जाना।

आपको अतिरिक्त प्रतीक्षा समूह की आवश्यकता नहीं है। चैनल पहले से ही आवश्यक सिंक्रनाइज़ेशन क्षमताओं प्रदान करते हैं। आपको अपना डेटा पढ़ने के बाद चैनल को बंद करने की आवश्यकता है।

package main 

import "fmt" 

type T int 

func main() { 
    var slice []T 
    queue := make(chan T, 1) 

    // concurrently produce some data 
    for i := 0; i < 20; i++ { 
     go func(i int) { 
      queue <- T(i) 
     }(i) 
    } 

    remaining := 20 
    for t := range queue { 
     // This loop blocks until a new item is available in the channel. 
     // You can grow your slice here, but must also care to close the 
     // channel, when you decide that you obtained enough data. 
     if t != 13 { // because it is evil ;) 
      slice = append(slice, t) 
     } 
     if remaining--; remaining == 0 { 
      close(queue) // do not forget to close the channel 
     } 
    } 

    fmt.Printf("slice: %v, len: %v", slice, len(slice)) 
}