सिंक के लिए उदाहरण।


108

क्या यह उदाहरण sync.WaitGroupसही का उपयोग है ? यह अपेक्षित परिणाम देता है, लेकिन मैं wg.Add(4)और की स्थिति के बारे में अनिश्चित हूं wg.Done()। क्या यह एक बार में चार गोरोइन्ट्स को जोड़ने के लिए समझ में आता है wg.Add()?

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

package main

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

func dosomething(millisecs time.Duration, wg *sync.WaitGroup) {
    duration := millisecs * time.Millisecond
    time.Sleep(duration)
    fmt.Println("Function in background, duration:", duration)
    wg.Done()
}

func main() {
    var wg sync.WaitGroup
    wg.Add(4)
    go dosomething(200, &wg)
    go dosomething(400, &wg)
    go dosomething(150, &wg)
    go dosomething(600, &wg)

    wg.Wait()
    fmt.Println("Done")
}

परिणाम (उम्मीद के अनुसार):

Function in background, duration: 150ms
Function in background, duration: 200ms
Function in background, duration: 400ms
Function in background, duration: 600ms
Done

1
क्या होगा अगर dosomething () इससे पहले कि यह wg.Done () कर सकता है क्रैश?
मोस्टोव्स्की

19
मुझे लगता है कि यह पुराना है, लेकिन भविष्य के लोगों के लिए, मैं defer wg.Done()समारोह की शुरुआत में एक प्रारंभिक कॉल की सिफारिश करूंगा ।
ब्रायन

जवाबों:


154

हां, यह उदाहरण सही है। यह महत्वपूर्ण है कि दौड़ की स्थिति को रोकने के wg.Add()लिए goबयान से पहले होता है । निम्नलिखित भी सही होगा:

func main() {
    var wg sync.WaitGroup
    wg.Add(1)
    go dosomething(200, &wg)
    wg.Add(1)
    go dosomething(400, &wg)
    wg.Add(1)
    go dosomething(150, &wg)
    wg.Add(1)
    go dosomething(600, &wg)

    wg.Wait()
    fmt.Println("Done")
}

हालाँकि, wg.Addबार-बार कॉल करना व्यर्थ है जब आप पहले से ही जानते हैं कि इसे कितनी बार कहा जाएगा।


Waitgroupsघबराहट अगर काउंटर शून्य से नीचे आती है। काउंटर शून्य पर शुरू होता है, प्रत्येक Done()एक है -1और प्रत्येक Add()पैरामीटर पर निर्भर करता है। तो, यह सुनिश्चित करने के लिए कि काउंटर नीचे कभी नहीं गिरता है और आतंक से बचने के लिए, आपको पहले आने की गारंटी दीAdd() जानी चाहिए ।Done()

गो में, ऐसी गारंटी मेमोरी मॉडल द्वारा दी जाती है ।

मेमोरी मॉडल में कहा गया है कि एक ही गोरोइन में सभी बयान उसी क्रम में निष्पादित किए जाते हैं जैसे वे लिखे जाते हैं। यह संभव है कि वे वास्तव में उस क्रम में नहीं होंगे, लेकिन परिणाम ऐसा होगा जैसे कि यह था। यह भी गारंटी है कि एक गोरोइन goबयान के बाद तक नहीं चलता है जो इसे कहता है । के बाद से Add()पहले होता है goबयान और goबयान से पहले होता है Done(), हम जानते हैं Add()से पहले होता हैDone()

यदि आप goस्टेटमेंट पहले आए थे Add(), तो प्रोग्राम सही तरीके से संचालित हो सकता है। हालाँकि, यह एक दौड़ की स्थिति होगी क्योंकि इसकी गारंटी नहीं होगी।


9
मेरा इस पर एक सवाल है: क्या यह बेहतर नहीं होगा defer wg.Done()ताकि हम सुनिश्चित हों कि यह उस मार्ग के बावजूद कहा जाता है जो गोरोइन लेता है? धन्यवाद।
एलेसांड्रो शांतििनी

2
यदि आप विशुद्ध रूप से यह सुनिश्चित करना चाहते हैं कि सभी रुटीन समाप्त होने से पहले फ़ंक्शन वापस नहीं आया है, तो हाँ डिफर को प्राथमिकता दी जाएगी। बस आम तौर पर एक प्रतीक्षा समूह का पूरा बिंदु तब तक इंतजार करना होता है जब तक कि सभी काम पूरा नहीं हो जाता है, तब उन परिणामों के साथ कुछ करें जो आप इंतजार कर रहे थे।
ज़ानवेन

1
यदि आप उपयोग नहीं करते हैं deferऔर आपका एक गॉरटॉइन कॉल करने में विफल रहता है wg.Done()... तो क्या आपका Waitबस हमेशा के लिए ब्लॉक नहीं होगा? ऐसा लगता है कि यह आसानी से अपने कोड में एक मुश्किल से मिल बग ...
Esparza

29

मैं wg.Add()कॉल को अंदर एम्बेड करने की सलाह दूंगाdoSomething() फ़ंक्शन में ही , ताकि यदि आप इसे कहे जाने वाले समय की संख्या को समायोजित करते हैं, तो आपको मैन्युअल रूप से ऐड पैरामीटर को अलग से समायोजित करने की आवश्यकता नहीं है, जो कि यदि आप एक को अपडेट करते हैं तो एक त्रुटि हो सकती है लेकिन अपडेट करना भूल जाते हैं अन्य (इस तुच्छ उदाहरण में जो कि संभावना नहीं है, लेकिन फिर भी, मैं व्यक्तिगत रूप से इसे कोड री-यूज़ के लिए बेहतर अभ्यास मानता हूं)।

जैसा कि स्टीफन वेनबर्ग इस प्रश्न के उत्तर में बताते हैं , आपको गोफुन स्पॉन करने से पहले वेटग्रुप को बढ़ाना होगा , लेकिन आप doSomething()फंक्शन के अंदर ही गॉफुन स्पॉन को लपेटकर इसे आसानी से पूरा कर सकते हैं :

func dosomething(millisecs time.Duration, wg *sync.WaitGroup) {
    wg.Add(1)
    go func() {
        duration := millisecs * time.Millisecond
        time.Sleep(duration)
        fmt.Println("Function in background, duration:", duration)
        wg.Done()
    }()
}

तब आप इसे बिना goआह्वान के कह सकते हैं , जैसे:

func main() {
    var wg sync.WaitGroup
    dosomething(200, &wg)
    dosomething(400, &wg)
    dosomething(150, &wg)
    dosomething(600, &wg)
    wg.Wait()
    fmt.Println("Done")
}

खेल के मैदान के रूप में: http://play.golang.org/p/WZcprjpHa_


21
  • Mroth उत्तर के आधार पर छोटा सुधार
  • Done के लिए defer का उपयोग करना अधिक सुरक्षित है
  func dosomething(millisecs time.Duration, wg *sync.WaitGroup) {
  wg.Add(1)
  go func() {
      defer wg.Done()
      duration := millisecs * time.Millisecond
      time.Sleep(duration)
      fmt.Println("Function in background, duration:", duration)
  }()
}

func main() {
  var wg sync.WaitGroup
  dosomething(200, &wg)
  dosomething(400, &wg)
  dosomething(150, &wg)
  dosomething(600, &wg)
  wg.Wait()
  fmt.Println("Done")
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.