अतुल्यकालिक और समानांतर कार्यों को प्रबंधित करें

Complete

सी # डेवलपर्स के लिए, टास्क पैरेलल लाइब्रेरी (टीपीएल) समानांतर कोड लिखने का एक आसान तरीका प्रदान करता है। हालांकि, सभी कोड समांतरकरण के लिए उपयुक्त नहीं हैं। उदाहरण के लिए, यदि कोई लूप प्रत्येक पुनरावृत्ति पर केवल थोड़ी मात्रा में काम करता है, या यह कई पुनरावृत्तियों के लिए नहीं चलता है, तो समांतरकरण का ओवरहेड कोड को अधिक धीमी गति से चलाने का कारण बन सकता है। इसके अलावा, समांतरकरण, किसी भी मल्टीथ्रेडेड कोड की तरह, आपके प्रोग्राम निष्पादन में जटिलता जोड़ता है।

डेटा और कार्य समांतरता में सामान्य नुकसान

कई मामलों में, Parallel.For और Parallel.ForEach साधारण अनुक्रमिक छोरों पर महत्वपूर्ण प्रदर्शन सुधार प्रदान कर सकते हैं। हालांकि, लूप को समानांतर करने का काम जटिलता का परिचय देता है जो उन समस्याओं को जन्म दे सकता है जो अनुक्रमिक कोड में आम नहीं हैं।

यह मत समझो कि समानांतर हमेशा तेज होता है

कुछ मामलों में एक समानांतर लूप अपने अनुक्रमिक समकक्ष की तुलना में धीमी गति से चल सकता है। अंगूठे का मूल नियम यह है कि समानांतर लूप जिनमें कुछ पुनरावृत्तियां और तेज़ उपयोगकर्ता प्रतिनिधि होते हैं, उनमें बहुत तेजी आने की संभावना नहीं होती है। हालाँकि, क्योंकि प्रदर्शन में कई कारक शामिल होते हैं, इसलिए हम अनुशंसा करते हैं कि आप हमेशा वास्तविक परिणामों का मूल्यांकन करें.

साझा स्मृति स्थानों पर लिखने से बचें

अनुक्रमिक कोड में, स्थिर चर या वर्ग क्षेत्रों से पढ़ना या लिखना असामान्य नहीं है। हालांकि, जब भी कई धागे समवर्ती रूप से ऐसे चर तक पहुंच रहे हैं, तो दौड़ की स्थिति के लिए एक महत्वपूर्ण संभावना है। भले ही आप चर तक पहुंच को सिंक्रनाइज़ करने के लिए ताले का उपयोग कर सकते हैं, सिंक्रनाइज़ेशन की लागत प्रदर्शन को नुकसान पहुंचा सकती है। इसलिए, हम अनुशंसा करते हैं कि आप जितना संभव हो उतना समानांतर लूप में साझा स्थिति तक पहुंच से बचें, या कम से कम सीमित करें। ऐसा करने का सबसे अच्छा तरीका लूप निष्पादन के दौरान थ्रेड-स्थानीय स्थिति को स्टोर करने के लिए एक Parallel.For चर का Parallel.ForEach उपयोग करना है और System.Threading.ThreadLocal<T> जो ओवरलोड का उपयोग करना है।

अति-समांतरकरण से बचें

समानांतर लूप का उपयोग करके, आप स्रोत संग्रह विभाजन और कार्यकर्ता थ्रेड्स सिंक्रनाइज़ करने के ओवरहेड लागत लगाना। समांतरकरण के लाभ कंप्यूटर पर प्रोसेसर की संख्या से और सीमित हैं। केवल एक प्रोसेसर पर कई गणना-बाध्य धागे चलाकर प्राप्त करने के लिए कोई स्पीडअप नहीं है। इसलिए, आपको सावधान रहना चाहिए कि लूप को अधिक समानांतर न करें।

सबसे आम परिदृश्य जिसमें अति-समांतरकरण हो सकता है वह नेस्टेड लूप में है। ज्यादातर मामलों में, केवल बाहरी लूप को समानांतर करना सबसे अच्छा है जब तक कि निम्न में से एक या अधिक शर्तें लागू न हों:

  • आंतरिक लूप को लंबा माना जाता है।
  • आप प्रत्येक आदेश पर एक महंगी गणना कर रहे हैं।
  • लक्ष्य प्रणाली को प्रसंस्करण को समानांतर करके उत्पादित धागे की संख्या को संभालने के लिए पर्याप्त प्रोसेसर के लिए जाना जाता है।

सभी मामलों में, इष्टतम क्वेरी आकृति निर्धारित करने का सबसे अच्छा तरीका परीक्षण और माप करना है।

एसिंक और समानांतर कार्यों में अपवाद हैंडलिंग

जब आप कार्य समानांतर लायब्रेरी (TPL) कार्य को चलाने के लिए उपयोग करते हैं, अपवाद कई भिन्न तरीकों से हो सकती हैं। सबसे आम तब होता है जब कोई कार्य अपवाद फेंकता है। अपवाद फेंकना तब हो सकता है जब कार्य थ्रेड पूल थ्रेड पर चल रहा हो या जब यह मुख्य थ्रेड पर चल रहा हो। किसी भी मामले में, अपवाद को कॉलिंग थ्रेड पर वापस प्रचारित किया जाता है।

जब आप किसी कार्य को Task.Wait पूरा करने के लिए प्रतीक्षा करने के लिए विधि का उपयोग करते हैं, कार्य द्वारा दिए गए किसी भी अपवाद वापस कॉलिंग थ्रेड के लिए प्रचारित किए जाते हैं। आप कोशिश/कैच ब्लॉक का उपयोग करके इन अपवादों को संभाल सकते हैं। यदि कोई कार्य अनुलग्न चाइल्ड कार्यों का पैरेंट है, या यदि आप एकाधिक कार्यों पर प्रतीक्षा कर रहे हैं, तो एकाधिक अपवाद फेंके जा सकते हैं. यदि एक या अधिक अपवाद फेंके जाते हैं, तो वे एक AggregateException उदाहरण में लिपटे होते हैं।

अपवाद में AggregateException एक InnerExceptions संपत्ति होती है जिसे फेंके गए सभी मूल अपवादों की जांच करने के लिए गणना की जा सकती है, और प्रत्येक को व्यक्तिगत रूप से संभालती है (या संभालती नहीं)।

निम्न उदाहरण प्रदर्शित करता है कि किसी कार्य द्वारा फेंके गए अपवादों को कैसे हैंडल करें।


public static partial class Program
{
    public static void Main()
    {
        HandleThree();
    }
    
    public static void HandleThree()
    {
        var task = Task.Run(
            () => throw new CustomException("This exception is expected!"));

        try
        {
            task.Wait();
        }
        catch (AggregateException ae)
        {
            foreach (var ex in ae.InnerExceptions)
            {
                // Handle the custom exception.
                if (ex is CustomException)
                {
                    Console.WriteLine(ex.Message);
                }
                // Rethrow any other exception.
                else
                {
                    throw ex;
                }
            }
        }
    }
}

// Define the CustomException class
public class CustomException : Exception
{
    public CustomException(string message) : base(message) { }
}
// The example displays the following output:
//        This exception is expected!

इस उदाहरण में HandleThree , विधि एक कार्य बनाता है जो एक CustomException. ब्लॉक संग्रह के try/catch माध्यम से पकड़ता है AggregateException और पुनरावृति करता है InnerExceptions । यदि अपवाद प्रकार CustomExceptionका है, तो यह संदेश को कंसोल पर प्रिंट करता है। यदि यह किसी अन्य प्रकार का अपवाद है, तो यह इसे फेंक देता है।

आप विधि का उपयोग करके मूल अपवादों को भी हैंडल कर सकते हैं AggregateException.Handle । यह विधि एक प्रतिनिधि लेती है जिसे संग्रह में प्रत्येक अपवाद के लिए कहा जाता है InnerExceptions । यदि प्रतिनिधि सही लौटाता है, तो अपवाद को हैंडल किया गया माना जाता है और संग्रह से निकाल दिया जाता है. यदि यह गलत लौटाता है, तो अपवाद को फिर से फेंक दिया जाता है।

निम्न उदाहरण प्रदर्शित करता है कि किसी कार्य द्वारा फेंके गए अपवादों को हैंडल करने के Handle लिए विधि का उपयोग कैसे करें।


public static partial class Program
{
    public static void HandleFour()
    {
        var task = Task.Run(
            () => throw new CustomException("This exception is expected!"));

        try
        {
            task.Wait();
        }
        catch (AggregateException ae)
        {
            ae.Handle(ex =>
            {
                // Handle the custom exception.
                if (ex is CustomException)
                {
                    Console.WriteLine(ex.Message);
                    return true;
                }
                // Rethrow any other exception.
                return false;
            });
        }
    }
}

इस उदाहरण में HandleFour , विधि एक कार्य बनाता है जो एक CustomException. try/catch ब्लॉक विधि को पकड़ता है AggregateException और कॉल करता हैHandle। प्रतिनिधि जांचता है कि अपवाद प्रकार CustomExceptionका है या नहीं। यदि अपवाद प्रकार CustomExceptionका है, तो प्रतिनिधि संदेश को कंसोल पर प्रिंट करता है और लौटाता है true। की true प्रतिक्रिया इंगित करती है कि अपवाद को संभाला गया है। यदि अपवाद किसी अन्य प्रकार का अपवाद है, तो प्रतिनिधि वापस आ जाता falseहै, जिससे अपवाद को फिर से फेंक दिया जाता है।

सारांश

यह इकाई उन स्थितियों का वर्णन करती है जब कोड समांतरकरण के लिए उपयुक्त नहीं होता है और डेटा और कार्य समांतरता में सामान्य नुकसान पर चर्चा करता है। उदाहरण के लिए, समानांतर मानना हमेशा तेज होता है, साझा स्मृति स्थानों पर लिखना, और अति-समांतरकरण। सामग्री यह भी बताती है कि एसिंक और समानांतर कार्यों में अपवादों को कैसे संभालना है, जिसमें विधि और विधि का उपयोग Task.Wait कैसे करना शामिल है AggregateException.Handle

प्रमुख बिंदु

  • सभी कोड समांतरकरण के लिए उपयुक्त नहीं हैं। कोड को समानांतर करने से पहले प्रदर्शन का परीक्षण और मापना आवश्यक है।
  • डेटा और कार्य समांतरता में सामान्य नुकसान में समानांतर को हमेशा तेज मानना, साझा स्मृति स्थानों पर लिखना और अति-समांतरकरण शामिल है।
  • एसिंक और समानांतर कार्यों में अपवादों को Task.Wait विधि और विधि का उपयोग करके नियंत्रित किया जा सकता है AggregateException.Handle
  • अपवाद में AggregateException एक InnerExceptions संपत्ति है जिसे फेंके गए सभी मूल अपवादों की जांच करने के लिए गणना की जा सकती है।