c# - एक async विधि द्वारा फेंक दिया एक अपवाद पकड़ो




asynchronous exception-handling (4)

.NET के लिए माइक्रोसॉफ्ट से एसिंक सीटीपी का उपयोग करना, क्या कॉलिंग विधि में एसिंक विधि द्वारा फेंक दिया गया अपवाद पकड़ना संभव है?

public async void Foo()
{
    var x = await DoSomethingAsync();

    /* Handle the result, but sometimes an exception might be thrown.
       For example, DoSomethingAsync gets data from the network
       and the data is invalid... a ProtocolException might be thrown. */
}

public void DoFoo()
{
    try
    {
        Foo();
    }
    catch (ProtocolException ex)
    {
          /* The exception will never be caught.
             Instead when in debug mode, VS2010 will warn and continue.
             The deployed the app will simply crash. */
    }
}

तो मूल रूप से मैं एसिंक कोड से अपवाद चाहता हूं कि मेरे कॉलिंग कोड में बबल हो जाए, यदि यह भी संभव है।


अपवाद को एसिंक फ़ंक्शन में पकड़ा जा सकता है।

public async void Foo()
{
    try
    {
        var x = await DoSomethingAsync();
        /* Handle the result, but sometimes an exception might be thrown
           For example, DoSomethingAsync get's data from the network
           and the data is invalid... a ProtocolException might be thrown */
    }
    catch (ProtocolException ex)
    {
          /* The exception will be caught here */
    }
}

public void DoFoo()
{
    Foo();
}

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

यह एक आसान समाधान खुलता है यदि आप विधि हस्ताक्षर बदल सकते हैं - Foo() को बदल दें ताकि यह प्रकार Task DoFoo() और फिर DoFoo() इस कोड में await Foo() कर सके:

public async Task Foo() {
    var x = await DoSomethingThatThrows();
}

public async void DoFoo() {
    try {
        await Foo();
    } catch (ProtocolException ex) {
        // This will catch exceptions from DoSomethingThatThrows
    }
}

यह पढ़ने के लिए कुछ हद तक अजीब है लेकिन हां, अपवाद कॉलिंग कोड पर बुलबुला होगा - लेकिन केवल अगर आप await या Wait() को Foo को कॉल करें

public async void DoFoo()
{
    try
    {
        await Foo();
    }
    catch (ProtocolException ex)
    {
          // The exception will be caught because you've awaited
          // the call in an async method.
    }
}

//or//

public void DoFoo()
{
    try
    {
        Foo().Wait();
    }
    catch (ProtocolException ex)
    {
          /* The exception will be caught because you've
             waited for the completion of the call. */
    }
} 

Async शून्य विधियों में अलग-अलग त्रुटि-हैंडलिंग अर्थशास्त्र हैं। जब एक एसिंक टास्क या एसिंक कार्य विधि से अपवाद फेंक दिया जाता है, तो उस अपवाद को टास्क ऑब्जेक्ट पर कैप्चर किया जाता है। एसिंक शून्य विधियों के साथ, कोई कार्य ऑब्जेक्ट नहीं है, इसलिए एसिंक शून्य विधि से बाहर किए गए किसी भी अपवाद को सीधे सिंक्रनाइज़ेशन कॉन्टेक्स्ट पर उठाया जाएगा जो एसिंक शून्य विधि प्रारंभ होने पर सक्रिय था। - https://msdn.microsoft.com/en-us/magazine/jj991977.aspx

ध्यान दें कि प्रतीक्षा () का उपयोग करने से आपका एप्लिकेशन अवरुद्ध हो सकता है, यदि .NET आपकी विधि को सिंक्रनाइज़ तरीके से निष्पादित करने का निर्णय लेता है।

यह स्पष्टीकरण http://www.interact-sw.co.uk/iangblog/2010/11/01/csharp5-async-exceptions बहुत अच्छा है - यह इस जादू को प्राप्त करने के लिए संकलक के चरणों पर चर्चा करता है।


यह ब्लॉग आपकी समस्या को अच्छी तरह से असिनक बेस्ट प्रैक्टिस बताता है।

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

सर्वश्रेष्ठ अभ्यास वापसी प्रकार को कार्य में बदलना होगा। साथ ही, एसिंक को हर तरह से कोड करने की कोशिश करें, प्रत्येक async विधि कॉल करें और async विधियों से कॉल किया जाए। कंसोल में एक मुख्य विधि को छोड़कर, जो एसिंक नहीं हो सकता है।

यदि आप इस सर्वोत्तम अभ्यास को अनदेखा करते हैं तो आप जीयूआई और एएसपी.नेट अनुप्रयोगों के साथ डेडलॉक्स में भाग लेंगे। डेडलॉक होता है क्योंकि ये अनुप्रयोग एक संदर्भ पर चलते हैं जो केवल एक थ्रेड की अनुमति देता है और इसे एसिंक थ्रेड पर छोड़ नहीं देगा। इसका मतलब है कि जीयूआई रिटर्न के लिए सिंक्रनाइज़ रूप से इंतजार कर रहा है, जबकि एसिंक विधि संदर्भ के लिए इंतजार कर रही है: डेडलॉक।

यह व्यवहार कंसोल एप्लिकेशन में नहीं होगा, क्योंकि यह थ्रेड पूल के संदर्भ में चलता है। Async विधि एक और थ्रेड पर वापस आ जाएगी जो निर्धारित किया जाएगा। यही कारण है कि एक परीक्षण कंसोल ऐप काम करेगा, लेकिन वही कॉल अन्य अनुप्रयोगों में डेडलॉक होगा ...





task-parallel-library