2012-09-26 11 views
34

मैं समानांतर में एक धीमी webservice कॉल कर रहा हूँ। हालात तब तक महान थे जब तक मुझे एहसास हुआ कि मुझे सेवा से कुछ जानकारी वापस लेने की जरूरत है। लेकिन मुझे नहीं लगता कि मूल्यों को वापस कहां प्राप्त करें। मैं डेटाबेस में नहीं लिख सकता, HttpContext.Current समानांतर का उपयोग करने वाली विधि के अंदर शून्य दिखाई देता है। फोरेचमैं समानांतर से वापसी मूल्य कैसे एकत्र करूं? ForEach?

नीचे एक नमूना प्रोग्राम है (आपके दिमाग में, कृपया स्ट्रिंग कॉन्सटेनेशन की बजाय धीमी वेब सेवा की कल्पना करें)

using System; 
using System.Threading.Tasks; 

class Program 
{ 
    static void Main(string[] args) 
    { 
     WordMaker m = new WordMaker(); 
     m.MakeIt(); 
    } 
    public class WordMaker 
    { 
     public void MakeIt() 
     { 
      string[] words = { "ack", "ook" }; 
      ParallelLoopResult result = Parallel.ForEach(words, word => AddB(word)); 
      Console.WriteLine("Where did my results go?"); 
      Console.ReadKey(); 
     } 
     public string AddB(string word) 
     { 
      return "b" + word; 
     } 
    } 

} 
+0

'Parallel.ForEach' का एक अलग अधिभार हो सकता है कि आप क्या चाहते: http://msdn.microsoft.com/en-us/library/ dd991486.aspx –

+0

दुर्भाग्य से यह वास्तव में ऐसा कुछ नहीं है जिसे आप ऐसा कर सकते हैं। 'समानांतर। Foreach()' सिर्फ रिटर्न का ट्रैक रखने के लिए नहीं बनाया गया था। हालांकि, मैं आपके 'AddB' फ़ंक्शन में' रेफरी पैरामीटर 'का उपयोग करने का सुझाव दूंगा। ऐसा हो सकता है। –

+0

@PhillipSchmidt: उदाहरण में प्रयुक्त अधिभार के साथ नहीं ... –

उत्तर

44

आपने इसे यहां छोड़ दिया है।

ParallelLoopResult result = Parallel.ForEach(words, word => AddB(word)); 

आप शायद कुछ की तरह चाहते हैं,

ParallelLoopResult result = Parallel.ForEach(words, word => 
{ 
    string result = AddB(word); 
    // do something with result 
}); 

आप इस के अंत में संग्रह के कुछ प्रकार चाहते हैं, System.Collections.Concurrent के तहत संग्रह में से एक का उपयोग कर, ConcurrentBag

var resultCollection = new ConcurrentBag<string>(); 
ParallelLoopResult result = Parallel.ForEach(words, word => 
{ 
    resultCollectin.Add(AddB(word)); 
}); 

// Do something with result 
+28

मुझे लगता है कि समानांतर LoopResult यहां कुछ भी उपयोगी नहीं है। +1 हालांकि – usr

+1

। LINQ में एक समानांतर() बहुत बेहतर होगा –

+0

क्या यह थ्रेड सुरक्षित कई धागे से एक सूची में जोड़ रहा है? –

11
पर विचार

परिणाम एकत्र करने के लिए ConcurrentBag का उपयोग न करें क्योंकि यह बेहद धीमा है। इसके बजाए स्थानीय लॉक का उपयोग करें।

var resultCollection = new List<string>(); 
object localLockObject = new object(); 

Parallel.ForEach<string, List<string>>(
     words, 
    () => { return new List<string>(); }, 
     (word, state, localList) => 
     { 
     localList.Add(AddB(word)); 
     return localList; 
     }, 
     (finalResult) => { lock (localLockObject) resultCollection.AddRange(finalResult); } 
); 

// Do something with resultCollection here 
+0

पर बढ़ाया जा सकता है क्या आपके पास यह दिखाने के लिए कोई आंकड़े हैं कि ConcurrentBag हमारे ऑब्जेक्ट लॉक का उपयोग करने से धीमा है? मैं सिर्फ यह जानना चाहता हूं कि यह कितना धीमा है, क्योंकि यह ऑब्जेक्ट लॉक का उपयोग करने से मेरा कोड क्लीनर दिखता है। –

+0

@dineshygv IMHO अंतर नगण्य है http://stackoverflow.com/questions/2950955/concurrentbagof-mytype-vs-listof-mytype/34016915#34016915 –

+0

या किसी भी लॉकिंग का उपयोग न करें ;-) – Steves

2

कैसे कुछ इस तरह के बारे में:

public class WordContainer 
{ 
    public WordContainer(string word) 
    { 
     Word = word; 
    } 

    public string Word { get; private set; } 
    public string Result { get; set; } 
} 

public class WordMaker 
{ 
    public void MakeIt() 
    { 
     string[] words = { "ack", "ook" }; 
     List<WordContainer> containers = words.Select(w => new WordContainer(w)).ToList(); 

     Parallel.ForEach(containers, AddB); 

     //containers.ForEach(c => Console.WriteLine(c.Result)); 
     foreach (var container in containers) 
     { 
      Console.WriteLine(container.Result); 
     } 

     Console.ReadKey(); 
    } 

    public void AddB(WordContainer container) 
    { 
     container.Result = "b" + container.Word; 
    } 
} 

मेरा मानना ​​है कि जब तक आप एक दूसरे के (के साथ बातचीत करने की जरूरत है परिणाम ताला या समवर्ती वस्तुओं आवश्यक नहीं है जैसे आप एक राशि की गणना कर रहे थे या संयोजन सभी शब्द)। इस मामले में फॉरएच आपकी मूल सूची को अच्छी तरह से तोड़ देता है और प्रत्येक थ्रेड को अपने स्वयं के ऑब्जेक्ट को हाथ से रखता है कि यह अन्य धागे के साथ हस्तक्षेप करने के बारे में चिंता किए बिना सभी चीजों को कुशल बना सकता है।

+0

हां, वह होगा कंसोल ऐप्स के लिए काम करें, लेकिन कंसोल ऐप्स के लिए ईवेंट आप संग्रह में पहले उन्हें जोड़ना चाहते हैं, अन्यथा आप कंसोल विंडो में इंटरलीव किए गए परिणाम प्राप्त कर सकते हैं। – MatthewMartin

+0

कंसोल। राइटलाइन कमांड मुख्य थ्रेड पर सिंक्रनाइज़ रूप से चल रहे हैं और यह परिणामों को समानांतर के बाद मूल सूची में परिभाषित क्रम में प्रिंट करेगा। फॉरएच सभी सूची आइटमों और रिटर्न को संसाधित करने के लिए समाप्त होता है। अगर मैं समानांतर के भीतर से WriteLine को बुला रहा था। तो फिर हां परिणाम अंतःस्थापित हो जाएंगे। – MichaC

11

AsParallelIEnumerable की विस्तार विधि का उपयोग करने पर विचार कर सकते हैं, यह आपके लिए समेकन का ख्याल रखेगा और परिणाम एकत्र करेगा।

words.AsParallel().Select(AddB).ToArray()

तुल्यकालन (जैसे ताले या समवर्ती संग्रह है कि ताले का उपयोग करें) आमतौर पर समवर्ती एल्गोरिदम के टोंटी हैं। जितना संभव हो सके सिंक्रनाइज़ेशन से बचने के लिए सबसे अच्छा है। मुझे लगता है कि AsParallel एक सिंगल थ्रेड पर उत्पादित सभी वस्तुओं को एक स्थानीय गैर-समवर्ती संग्रह में डालने और फिर अंत में इन्हें संयोजन करने जैसे कुछ स्मार्ट का उपयोग करता है।

+1

यह काफी बेहतर है। –

1

यह सुरक्षित, तेज, और सरल लगता है:

public string[] MakeIt() { 
     string[] words = { "ack", "ook" }; 
     string[] results = new string[words.Length]; 
     ParallelLoopResult result = 
      Parallel.For(0, words.Length, i => results[i] = AddB(words[i])); 
     return results; 
    } 
+0

यह कैश पिंग-पोंग का कारण बन सकता है, हालांकि समवर्ती संग्रह से अभी भी काफी बेहतर है। – Steves

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