क्या Parallel.ForEach सक्रिय थ्रेड्स की संख्या को सीमित करता है?


107

इस कोड को दिया:

var arrayStrings = new string[1000];
Parallel.ForEach<string>(arrayStrings, someString =>
{
    DoSomething(someString);
});

क्या सभी 1000 धागे लगभग एक साथ घूमेंगे?

जवाबों:


149

नहीं, यह 1000 थ्रेड शुरू नहीं करेगा - हाँ, यह सीमित करेगा कि कितने थ्रेड का उपयोग किया जाता है। समानांतर एक्सटेंशन उचित संख्या में कोर का उपयोग करता है, जो इस आधार पर करता है कि आपके पास कितने भौतिक हैं और कितने पहले से ही व्यस्त हैं। यह प्रत्येक कोर के लिए काम आवंटित करता है और फिर एक तकनीक का उपयोग करता है जिसे काम चोरी कहा जाता है ताकि प्रत्येक थ्रेड प्रक्रिया को कुशलता से अपनी कतार बनाने के लिए और केवल किसी भी महंगी क्रॉस-थ्रेड पहुंच की आवश्यकता होती है जब इसे वास्तव में करने की आवश्यकता होती है।

यह कैसे काम आवंटित करता है और अन्य सभी विषयों के बारे में जानकारी के भार के लिए PFX टीम ब्लॉग पर एक नज़र है ।

ध्यान दें कि कुछ मामलों में आप जो समानता चाहते हैं, उसकी डिग्री भी निर्दिष्ट कर सकते हैं।


2
मैं Parallel.ForEach (FilePathArray, path => ... का उपयोग कर रहा था, आज रात को पढ़ने के लिए प्रत्येक फ़ाइल के लिए एक नई फ़ाइल बनाने वाली 24,000 फ़ाइलों के बारे में। बहुत ही सरल कोड। ऐसा प्रतीत होता है कि 6 धागे भी 7200 RPM डिस्क को अभिभूत करने के लिए पर्याप्त थे। मैं 100% उपयोग से पढ़ रहा था। कुछ घंटों की अवधि में मैंने 10,000 थ्रेड्स पर समानांतर लाइब्रेरी स्पिन को देखा। मैंने MaxDegreeOfParallelism का उपयोग करके परीक्षण किया और निश्चित रूप से 8000+ थ्रेड गायब हो गए। मैंने इसे अब एक ही समय के साथ कई बार परीक्षण किया है। परिणाम।
जेक ड्रू

यह कुछ पतित 'DoSomething' के लिए 1000 सूत्र शुरू कर सकता है । (जैसा कि मैं वर्तमान में उत्पादन कोड में एक समस्या से जूझ रहा हूं, जो एक सीमा निर्धारित करने में विफल रहा और 200+ थ्रेड्स जिससे SQL कनेक्शन पूल पॉप हो रहा है .. मैं किसी भी काम के लिए अधिकतम डीओपी सेट करने की सलाह देता हूं जो तुच्छ रूप से तर्क नहीं किया जा सकता है स्पष्ट रूप से सीपीयू बाउंड होने के बारे में।)
user2864740


28

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

मूल रूप से सभी विभिन्न समानांतर लाइब्रेरी फ़ंक्शंस के पीछे थ्रेड पूल, उपयोग करने के लिए अधिकतम संख्या में थ्रेड्स का काम करेगा। भौतिक प्रोसेसर कोर की संख्या समीकरण का केवल एक हिस्सा है। कोर की संख्या और थ्रेड की संख्या के बीच एक से एक संबंध नहीं है।

मुझे रद्द करने और थ्रेड को सिंक्रोनाइज़ करने में सहायक बहुत उपयोगी नहीं है। उम्मीद है कि MS MSDN में बेहतर उदाहरणों की आपूर्ति कर सकता है।

मत भूलो, शरीर कोड को कई थ्रेड पर चलाने के लिए लिखा जाना चाहिए, साथ ही सभी सामान्य थ्रेड सुरक्षा कारणों के साथ, फ्रेमवर्क उस कारक को सार नहीं करता है ... अभी तक।


1
".. ForEach का मुख्य भाग लंबे समय तक चलने वाले अवरुद्ध कार्यों को बुलाता है, जो थ्रेड को प्रतीक्षा में छोड़ देता है, एल्गोरिथ्म अधिक थ्रेड्स को फैलाएगा .." - पतित मामलों में इसका मतलब है कि अनुमति के अनुसार बनाए गए कई थ्रेड हो सकते हैं। थ्रेडपूल प्रति।
user2864740

2
आप सही हैं, IO के लिए यह +100 धागे आवंटित कर सकता है क्योंकि मैंने खुद को डिबग किया है
FindOutIslamNow

5

यह प्रोसेसर / कोर की संख्या के आधार पर एक इष्टतम संख्या में थ्रेड्स का काम करता है। वे सभी एक ही बार में नहीं जाएंगे।


5

क्या देखें समानांतर। इसके लिए प्रति कार्य एक कार्य का उपयोग किया जाता है? उपयोग करने के लिए एक "मानसिक मॉडल" के विचार के लिए। हालांकि लेखक यह बताता है कि "दिन के अंत में, यह याद रखना महत्वपूर्ण है कि कार्यान्वयन विवरण किसी भी समय बदल सकता है।"


4

बड़ा सवाल है। आपके उदाहरण में, क्वाड कोर प्रोसेसर पर भी समानांतर का स्तर बहुत कम है, लेकिन कुछ प्रतीक्षा के साथ समानांतरकरण का स्तर काफी अधिक हो सकता है।

// Max concurrency: 5
[Test]
public void Memory_Operations()
{
    ConcurrentBag<int> monitor = new ConcurrentBag<int>();
    ConcurrentBag<int> monitorOut = new ConcurrentBag<int>();
    var arrayStrings = new string[1000];
    Parallel.ForEach<string>(arrayStrings, someString =>
    {
        monitor.Add(monitor.Count);
        monitor.TryTake(out int result);
        monitorOut.Add(result);
    });

    Console.WriteLine("Max concurrency: " + monitorOut.OrderByDescending(x => x).First());
}

अब देखें कि क्या होता है जब एक HTTP अनुरोध को अनुकरण करने के लिए एक प्रतीक्षा ऑपरेशन जोड़ा जाता है।

// Max concurrency: 34
[Test]
public void Waiting_Operations()
{
    ConcurrentBag<int> monitor = new ConcurrentBag<int>();
    ConcurrentBag<int> monitorOut = new ConcurrentBag<int>();
    var arrayStrings = new string[1000];
    Parallel.ForEach<string>(arrayStrings, someString =>
    {
        monitor.Add(monitor.Count);

        System.Threading.Thread.Sleep(1000);

        monitor.TryTake(out int result);
        monitorOut.Add(result);
    });

    Console.WriteLine("Max concurrency: " + monitorOut.OrderByDescending(x => x).First());
}

मैंने अभी तक कोई बदलाव नहीं किया है और समवर्ती / समानांतरकरण के स्तर में नाटकीय रूप से उछाल आया है। संगामिति के साथ इसकी सीमा बढ़ सकती है ParallelOptions.MaxDegreeOfParallelism

// Max concurrency: 43
[Test]
public void Test()
{
    ConcurrentBag<int> monitor = new ConcurrentBag<int>();
    ConcurrentBag<int> monitorOut = new ConcurrentBag<int>();
    var arrayStrings = new string[1000];
    var options = new ParallelOptions {MaxDegreeOfParallelism = int.MaxValue};
    Parallel.ForEach<string>(arrayStrings, options, someString =>
    {
        monitor.Add(monitor.Count);

        System.Threading.Thread.Sleep(1000);

        monitor.TryTake(out int result);
        monitorOut.Add(result);
    });

    Console.WriteLine("Max concurrency: " + monitorOut.OrderByDescending(x => x).First());
}

// Max concurrency: 391
[Test]
public void Test()
{
    ConcurrentBag<int> monitor = new ConcurrentBag<int>();
    ConcurrentBag<int> monitorOut = new ConcurrentBag<int>();
    var arrayStrings = new string[1000];
    var options = new ParallelOptions {MaxDegreeOfParallelism = int.MaxValue};
    Parallel.ForEach<string>(arrayStrings, options, someString =>
    {
        monitor.Add(monitor.Count);

        System.Threading.Thread.Sleep(100000);

        monitor.TryTake(out int result);
        monitorOut.Add(result);
    });

    Console.WriteLine("Max concurrency: " + monitorOut.OrderByDescending(x => x).First());
}

मैं सेटिंग को फिर से शुरू करता हूं ParallelOptions.MaxDegreeOfParallelism। यह आवश्यक रूप से उपयोग में धागे की संख्या में वृद्धि नहीं करेगा, लेकिन यह सुनिश्चित करेगा कि आप केवल एक थ्रेड की संख्या शुरू करें, जो आपकी चिंता का विषय है।

अंत में अपने प्रश्न का उत्तर देने के लिए, नहीं, आपको एक बार में शुरू करने के लिए सभी सूत्र नहीं मिलेंगे। यदि आप समानांतर दौड़ में पूरी तरह से दौड़ना चाहते हैं, तो Parallel.Invoke का उपयोग करें।

// 636462943623363344
// 636462943623363344
// 636462943623363344
// 636462943623363344
// 636462943623363344
// 636462943623368346
// 636462943623368346
// 636462943623373351
// 636462943623393364
// 636462943623393364
[Test]
public void Test()
{
    ConcurrentBag<string> monitor = new ConcurrentBag<string>();
    ConcurrentBag<string> monitorOut = new ConcurrentBag<string>();
    var arrayStrings = new string[1000];
    var options = new ParallelOptions {MaxDegreeOfParallelism = int.MaxValue};
    Parallel.ForEach<string>(arrayStrings, options, someString =>
    {
        monitor.Add(DateTime.UtcNow.Ticks.ToString());
        monitor.TryTake(out string result);
        monitorOut.Add(result);
    });

    var startTimes = monitorOut.OrderBy(x => x.ToString()).ToList();
    Console.WriteLine(string.Join(Environment.NewLine, startTimes.Take(10)));
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.