Parallel.ForEach बनाम Task.Run और Task.WhenAll


158

समानांतर रूप से कार्यों का एक सेट शुरू करने के लिए Parallel.ForEach या Task.Run () का उपयोग करने के बीच अंतर क्या हैं?

संस्करण 1:

List<string> strings = new List<string> { "s1", "s2", "s3" };
Parallel.ForEach(strings, s =>
{
    DoSomething(s);
});

संस्करण 2:

List<string> strings = new List<string> { "s1", "s2", "s3" };
List<Task> Tasks = new List<Task>();
foreach (var s in strings)
{
    Tasks.Add(Task.Run(() => DoSomething(s)));
}
await Task.WhenAll(Tasks);

3
मुझे लगता है कि 2 कोड का टुकड़ा लगभग 1 के बराबर होगा यदि आप Task.WaitAllइसके बजाय उपयोग करते हैं Task.WhenAll
avo

15
कृपया यह भी ध्यान दें कि दूसरा व्यक्ति DoSomething ("s3") का तीन बार प्रदर्शन करेगा और यह समान परिणाम नहीं देगा! stackoverflow.com/questions/4684320/…
Nullius


@ ध्यान दें: संस्करण 2 में एस्किंक / वेट का उपयोग किया गया है, जिसका अर्थ है कि यह एक अलग प्रश्न है। एससंक / वेट को वीएस 2012 के साथ पेश किया गया था, संभावित डुप्लिकेट थ्रेड लिखे जाने के 1.5 साल बाद।
पीटर टी

जवाबों:


159

इस मामले में, दूसरी विधि अतुल्यकालिक रूप से अवरुद्ध होने के बजाय कार्यों के पूरा होने की प्रतीक्षा करेगी।

हालांकि, Task.Runलूप में उपयोग करने के लिए एक नुकसान है- साथ Parallel.ForEach, एक ऐसा भी है Partitionerजो आवश्यक से अधिक कार्य करने से बचने के लिए बनाया गया है। Task.Runहमेशा प्रति आइटम एक ही कार्य करेगा (जब से आप यह कर रहे हैं), लेकिन Parallelक्लास बैच काम करते हैं इसलिए आप कुल कार्य आइटम की तुलना में कम कार्य बनाते हैं। यह समग्र रूप से बेहतर प्रदर्शन प्रदान कर सकता है, खासकर अगर लूप बॉडी में प्रति आइटम थोड़ी मात्रा में काम हो।

यदि यह स्थिति है, तो आप दोनों विकल्पों को जोड़कर लिख सकते हैं:

await Task.Run(() => Parallel.ForEach(strings, s =>
{
    DoSomething(s);
}));

ध्यान दें कि यह इस छोटे रूप में भी लिखा जा सकता है:

await Task.Run(() => Parallel.ForEach(strings, DoSomething));

1
महान जवाब, मैं सोच रहा था कि क्या आप मुझे इस विषय पर एक अच्छी पठन सामग्री की ओर संकेत कर सकते हैं?
दिमित्र दिमित्रोव

@DimitarDimitrov सामान्य TPL सामान के लिए, reedcopsey.com/series/parallelism-in-net4
रीड

1
मेरा पैरेलल.फोरच निर्माण मेरे एप्लिकेशन को क्रैश कर रहा था। मैं इसके अंदर कुछ भारी इमेज प्रोसेसिंग कर रहा था। हालाँकि, जब मैंने टास्क जोड़ा। Run (() => Parallel.ForEach (....)); यह दुर्घटनाग्रस्त हो गया। क्या आप व्यख्या कर सकते हैं? कृपया ध्यान दें कि मैं सिस्टम पर कोर की संख्या के समानांतर विकल्पों को विवश करता हूं।
बंदरबम्प्स

3
क्या होगा अगर DoSomethingहै async void DoSomething?
फ्रांसेस्को बोनिज़ी

1
किस बारे में async Task DoSomething?
शॉन मैकलीन

37

पहला संस्करण कॉलिंग थ्रेड को ब्लॉक करेगा (और उस पर कुछ कार्यों को चलाएगा)।
अगर यह UI थ्रेड है, तो यह UI को फ्रीज कर देगा।

दूसरा संस्करण थ्रेड पूल में असिंक्रोनस रूप से कार्यों को चलाएगा और कॉलिंग थ्रेड को तब तक जारी करेगा जब तक वे काम नहीं करते।

शेड्यूल किए गए एल्गोरिदम में उपयोग किए गए अंतर भी हैं।

ध्यान दें कि आपके दूसरे उदाहरण को छोटा किया जा सकता है

await Task.WhenAll(strings.Select(s => Task.Run(() => DoSomething(s)));

2
क्या यह नहीं होना चाहिए await Task.WhenAll(strings.Select(async s => await Task.Run(() => DoSomething(s)));? कार्यों को वापस करने के दौरान मुझे परेशानी हुई (प्रतीक्षा करने के बजाय) विशेष रूप से तब जब usingवस्तुओं को निपटाने के लिए बयान शामिल थे।
मार्टिन कोल

मेरा Parallel.ForEach कॉल मेरे UI को क्रैश कर रहा था जिससे मैंने Task जोड़ा था ।un () => Parallel.ForEach (....)); यह करने के लिए और यह दुर्घटनाग्रस्त हो गया हल।
बंदरजप्स

1

मैंने इसे करना समाप्त कर दिया, क्योंकि यह पढ़ने में आसान लगा:

  List<Task> x = new List<Task>();
  foreach(var s in myCollectionOfObject)
  {
      // Note there is no await here. Just collection the Tasks
      x.Add(s.DoSomethingAsync());
  }
  await Task.WhenAll(x);

इस तरह से आप कार्य कर रहे हैं एक के बाद एक निष्पादित हो रहे हैं या जब सभी एक ही बार में शुरू कर रहे हैं?
विनीसियस गुल्बर्टो

जहां तक ​​मैं बता सकता हूं, वे सभी तब शुरू होते हैं जब मैं "DoSomethingAsync ()" कहता हूं। कुछ भी उन पर अवरुद्ध नहीं होता है, हालांकि, जब तक कि व्हेल को नहीं बुलाया जाता है।
क्रिस एम।

आपका मतलब है जब पहला "DoSomethingAsync ()" कहा जाता है?
विनीसियस गुलबर्टो

1
@ChrisM। यह DoSomethingAsync () के पहले इंतजार तक अवरुद्ध हो जाएगा क्योंकि यह वही है जो निष्पादन को आपके लूप में वापस स्थानांतरित करेगा। यदि यह समकालिक है और आपकी टास्क वापस आ जाती है, तो सभी कोड एक के बाद एक चलाए जाएंगे और जब सभी कार्य पूरा होने का इंतजार करेंगे
शमौन बेलांगर

0

मैंने देखा है Parallel.ForEach को अनुचित तरीके से इस्तेमाल किया गया है, और मुझे लगा कि इस प्रश्न में एक उदाहरण से मदद मिलेगी।

जब आप नीचे एक कंसोल ऐप में कोड चलाते हैं, तो आप देखेंगे कि Parallel.ForEach में निष्पादित कार्य कैसे कॉलिंग थ्रेड को ब्लॉक नहीं करते हैं। यदि आप परिणाम (सकारात्मक या नकारात्मक) की परवाह नहीं करते हैं तो यह ठीक हो सकता है लेकिन यदि आपको परिणाम की आवश्यकता है, तो आपको Task.WhenAll का उपयोग करना सुनिश्चित करना चाहिए।

using System;
using System.Linq;
using System.Threading.Tasks;

namespace ParrellelEachExample
{
    class Program
    {
        static void Main(string[] args)
        {
            var indexes = new int[] { 1, 2, 3 };

            RunExample((prefix) => Parallel.ForEach(indexes, (i) => DoSomethingAsync(i, prefix)),
                "Parallel.Foreach");

            Console.ForegroundColor = ConsoleColor.Yellow;
            Console.WriteLine("*You'll notice the tasks haven't run yet, because the main thread was not blocked*");
            Console.WriteLine("Press any key to start the next example...");
            Console.ReadKey();

            RunExample((prefix) => Task.WhenAll(indexes.Select(i => DoSomethingAsync(i, prefix)).ToArray()).Wait(),
                "Task.WhenAll");
            Console.WriteLine("All tasks are done.  Press any key to close...");
            Console.ReadKey();
        }

        static void RunExample(Action<string> action, string prefix)
        {
            Console.ForegroundColor = ConsoleColor.White;
            Console.WriteLine($"{Environment.NewLine}Starting '{prefix}'...");
            action(prefix);
            Console.WriteLine($"{Environment.NewLine}Finished '{prefix}'{Environment.NewLine}");
        }


        static async Task DoSomethingAsync(int i, string prefix)
        {
            await Task.Delay(i * 1000);
            Console.WriteLine($"Finished: {prefix}[{i}]");
        }
    }
}

यहाँ परिणाम है:

यहाँ छवि विवरण दर्ज करें

निष्कर्ष:

Parallel.ForEach का उपयोग टास्क के साथ करने से कॉलिंग थ्रेड ब्लॉक नहीं होगा। यदि आप परिणाम की परवाह करते हैं, तो कार्यों का इंतजार करना सुनिश्चित करें।

~ चीयर्स

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.