Parallel.ForEach vs Task.Factory.StartNew


267

नीचे दिए गए कोड स्निपेट्स के बीच क्या अंतर है? क्या दोनों धागे के धागे का उपयोग नहीं करेंगे?

उदाहरण के लिए, यदि मैं संग्रह में प्रत्येक आइटम के लिए कोई फ़ंक्शन कॉल करना चाहता हूं,

Parallel.ForEach<Item>(items, item => DoSomething(item));

vs

foreach(var item in items)
{
  Task.Factory.StartNew(() => DoSomething(item));
}

जवाबों:


302

पहला बेहतर विकल्प है।

Parallel.ForEach, आंतरिक रूप से, Partitioner<T>अपने संग्रह को कार्य आइटम में वितरित करने के लिए उपयोग करता है । यह प्रति आइटम एक कार्य नहीं करेगा, बल्कि इसमें शामिल ओवरहेड को कम करने के लिए बैच करेगा।

दूसरा विकल्प Taskआपके संग्रह में प्रति आइटम एकल शेड्यूल करेगा । जबकि परिणाम (लगभग) समान होंगे, यह विशेष रूप से बड़े संग्रह के लिए आवश्यक से कहीं अधिक ओवरहेड का परिचय देगा, और समग्र रनटाइम को धीमा कर देगा।

FYI करें - उपयोग किए गए विभाजन को Parallel.ForEach के लिए उपयुक्त अधिभार का उपयोग करके नियंत्रित किया जा सकता है , यदि ऐसा है तो वांछित। विवरण के लिए, MSDN पर कस्टम पार्टीशनर देखें ।

मुख्य अंतर, रनटाइम पर, दूसरा अतुल्यकालिक कार्य करेगा। इसे Parallel.ForEach का उपयोग करके डुप्लिकेट किया जा सकता है:

Task.Factory.StartNew( () => Parallel.ForEach<Item>(items, item => DoSomething(item)));

ऐसा करने से, आप अभी भी पार्टीशनर्स का लाभ उठाते हैं, लेकिन ऑपरेशन पूरा होने तक ब्लॉक नहीं करते हैं।


8
IIRC, Parallel.ForEach द्वारा किया गया डिफ़ॉल्ट विभाजन भी उपलब्ध हार्डवेयर थ्रेड्स की संख्या को ध्यान में रखता है, जिससे आपको प्रारंभ करने के लिए इष्टतम संख्या में कार्य करने से बचना पड़ता है। समानांतर प्रोग्रामिंग लेख के माइक्रोसॉफ्ट के पैटर्न की जाँच करें; यह सब इस में सामान की महान स्पष्टीकरण मिल गया है।
मल रॉस

2
@ मंगल: सॉर्ट ऑफ़ ... यह वास्तव में पार्टीशनर नहीं है, बल्कि टास्कस्क्रिडर का काम है। टास्कस्क्रिडर, डिफ़ॉल्ट रूप से, नए थ्रेडपूल का उपयोग करता है, जो अब इसे बहुत अच्छी तरह से संभालता है।
रीड कोपसे

धन्यवाद। मुझे पता था कि मुझे "मैं कोई विशेषज्ञ नहीं हूँ, लेकिन ..." को छोड़ दिया जाना चाहिए। :)
मल रॉस

@ReedCopsey: रैपर टास्क के लिए Parallel.ForEach के माध्यम से शुरू किए गए कार्यों को कैसे संलग्न करें? ताकि जब आप कॉल करें। एक आवरण कार्य पर। () एक समानांतर टास्क पूरा होने तक लटका रहता है?
कॉन्स्टेंटिन टर्कस

1
@ टार्कस यदि आप कई अनुरोध कर रहे हैं, तो आप प्रत्येक कार्य आइटम (अपने समानांतर लूप में) में सिर्फ HttpClient.GetString का उपयोग करके बेहतर हैं। आम तौर पर पहले से ही समवर्ती पाश के अंदर एक async विकल्प लगाने का कोई कारण नहीं है, ...
रीड कोपसे

89

मैंने "Parallel.For" के साथ "टास्क" ऑब्जेक्ट्स के साथ एक बार "1,000,000,000 (एक बिलियन)" विधि चलाने का एक छोटा सा प्रयोग किया।

मैंने प्रोसेसर समय को मापा और समानांतर को अधिक कुशल पाया। Parallel.For आपके कार्य को छोटे कार्य मदों में विभाजित करता है और सभी कोर पर उन्हें एक इष्टतम तरीके से निष्पादित करता है। बहुत से कार्य ऑब्जेक्ट बनाते समय (FYI TPL आंतरिक रूप से थ्रेड पूलिंग का उपयोग करेगा) प्रत्येक कार्य पर प्रत्येक निष्पादन को बॉक्स में अधिक तनाव पैदा करेगा जो नीचे दिए गए प्रयोग से स्पष्ट है।

मैंने एक छोटा सा वीडियो भी बनाया है जो बेसिक TPL की व्याख्या करता है और यह भी प्रदर्शित करता है कि Parallel.For सामान्य कार्यों और थ्रेड्स की तुलना में http://www.youtube.com/watch?v=No7QqSc5cl8 को अधिक कुशलतापूर्वक आपके कोर का उपयोग करता है ।

प्रयोग १

Parallel.For(0, 1000000000, x => Method1());

प्रयोग २

for (int i = 0; i < 1000000000; i++)
{
    Task o = new Task(Method1);
    o.Start();
}

प्रोसेसर समय की तुलना


यह अधिक कुशल होगा और धागे बनाने के पीछे का कारण महंगा है प्रयोग 2 एक बहुत बुरा अभ्यास है।
टिम

@ जॉर्जी-कृपया इस बात पर ध्यान दें कि क्या बुरा है।
शिवप्रसाद कोईराला

3
मुझे क्षमा करें, मेरी गलती है, मुझे स्पष्ट करना चाहिए था। मेरा मतलब है कि लूप में टास्क का निर्माण 1000000000 तक है। ओवरहेड अकल्पनीय है। यह उल्लेख करने के लिए नहीं कि समानांतर एक समय में 63 से अधिक कार्य नहीं बना सकता है, जो इसे मामले में बहुत अधिक अनुकूलित बनाता है।
जॉर्जी-

यह 1000000000 कार्यों के लिए सच है। हालाँकि जब मैं एक छवि संसाधित करता हूं (बार-बार, भग्न को ज़ूम करते हुए) और समानांतर करता हूं। तर्ज पर बहुत सारे कोर निष्क्रिय होते हैं जबकि अंतिम धागे के खत्म होने का इंतजार करते हैं। इसे तेज़ करने के लिए मैंने डेटा को 64 कार्य पैकेजों में स्वयं विभाजित किया और इसके लिए कार्य बनाए। (तब टास्क। पूरा करने के लिए प्रतीक्षा करने के लिए। किसी भी कार्य को पूरा करने के लिए 1-2 थ्रेड्स को समाप्त करने के लिए प्रतीक्षा करने के बजाय कार्य को पूरा करने में मदद करने के लिए आइडल थ्रेड्स को काम पैकेज लेने का विचार है।
टेड हेन्सन

1
Mehthod1()इस उदाहरण में क्या करता है?
जैपनीलिका

17

Parallel.ForEach अनुकूलन करेगा (लूप समाप्त होने तक नए थ्रेड शुरू भी नहीं कर सकता) और ब्लॉक करें, और Task.Factory स्पष्ट रूप से प्रत्येक आइटम के लिए एक नया कार्य उदाहरण बनाएगा, और समाप्त होने से पहले वापस आ जाएगा (अतुल्यकालिक कार्य)। Parallel.Foreach बहुत अधिक कुशल है।


11

मेरे विचार में सबसे यथार्थवादी परिदृश्य तब होता है जब कार्यों को पूरा करने के लिए एक भारी ऑपरेशन होता है। शिवप्रसाद का दृष्टिकोण कंप्यूटिंग की तुलना में ऑब्जेक्ट निर्माण / मेमोरी आवंटन पर अधिक केंद्रित है। मैंने निम्नलिखित विधि से एक शोध किया है:

public static double SumRootN(int root)
{
    double result = 0;
    for (int i = 1; i < 10000000; i++)
        {
            result += Math.Exp(Math.Log(i) / root);
        }
        return result; 
}

इस विधि के निष्पादन में लगभग 0.5 सेकेंड का समय लगता है।

मैंने इसे समानांतर का उपयोग करते हुए 200 बार बुलाया:

Parallel.For(0, 200, (int i) =>
{
    SumRootN(10);
});

फिर मैंने पुराने जमाने के तरीके का उपयोग करते हुए इसे 200 बार कहा:

List<Task> tasks = new List<Task>() ;
for (int i = 0; i < loopCounter; i++)
{
    Task t = new Task(() => SumRootN(10));
    t.Start();
    tasks.Add(t);
}

Task.WaitAll(tasks.ToArray()); 

पहला मामला 26656ms में पूरा हुआ, दूसरा 24478ms में। मैंने इसे कई बार दोहराया। हर बार दूसरा तरीका हाशिए पर है।


Parallel.For का उपयोग करना पुराने जमाने का तरीका है। कार्य की इकाइयों के लिए टास्क का उपयोग करने की सिफारिश की जाती है जो एक समान नहीं हैं। Microsoft MVP और TPL के डिज़ाइनरों ने यह भी उल्लेख किया है कि कार्य का उपयोग करते हुए थ्रेड्स का उपयोग अधिक कुशलता से किया जाएगा, ienot ब्लॉक को अन्य इकाइयों के पूरा होने की प्रतीक्षा करते समय।
सनकैट २२
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.