async

2012-09-07 21 views
18

से अनचाहे अपवादों को पकड़ें जब async विधि जो अपवाद फेंकने पर प्रतीक्षा की जाती है, अपवाद कहीं भी संग्रहीत होता है और इसे फेंकने में देरी होती है। WinForms या WPF अनुप्रयोग में, यह अपवाद फेंकने के लिए SynchronizationContext.Current का उपयोग करता है। हालांकि, उदाहरण में एक कंसोल एप्लिकेशन, यह थ्रेड पूल पर अपवाद फेंकता है और यह एप्लिकेशन को नीचे लाता है।async

मैं async विधि से आवेदन को कम करने से अपवाद को कैसे रोक सकता हूं?

संपादित करें:

appearantly मुद्दा मैं वर्णन कर रहा हूँ है, क्योंकि मैं voidasync तरीकों की है। टिप्पणी देखो।

+2

यदि एक async विधि का इंतजार है, तो अपवाद उस कोड में फेंक दिया गया है जो इसका इंतजार कर रहा है। अनचाहे अपवाद इस तरह से व्यवहार करते हैं यदि विधि 'शून्य' लौट रही है। जितना संभव हो सके 'async शून्य' विधियों का उपयोग करने से बचने का यह एक कारण है। – svick

+1

@svick 'async void' विधियों का निर्धारण निर्धारक व्यवहार में होता है; एक 'टास्क'-रिटर्निंग फ़ंक्शन को कॉल करना जिसके परिणामस्वरूप अपवाद होता है और कार्य का इंतजार नहीं करता है, भविष्य में कुछ सेमिनारम बिंदु पर 'UnobservedTaskException' ईवेंट को बढ़ाएगा जब कचरा कलेक्टर चलता है, और यदि ऐसा कुछ नहीं करता है, तो प्रोग्राम चुपचाप जारी रहेगा अगर सब ठीक है। समस्या 'async शून्य' विधियों के साथ नहीं है, वे केवल वास्तविक समस्या का पर्दाफाश करते हैं। यदि आप 'async' विधि से 'कार्य'-रिटर्निंग फ़ंक्शन को कॉल नहीं करते हैं, तो एक अच्छा मौका है कि आप कुछ गलत कर रहे हैं। – hvd

+0

वैसे, केवल "अच्छा मौका" क्योंकि कुछ असाधारण (कोई इरादा नहीं है) मामले हैं जहां अपवादों को त्यागना ठीक है, लेकिन पूरी तरह से नहीं। – hvd

उत्तर

10

जब async विधि प्रारंभ की गई है, तो यह वर्तमान सिंक्रनाइज़ेशन संदर्भ को कैप्चर करता है। इस समस्या को हल करने का एक तरीका है अपना स्वयं का सिंक्रनाइज़ेशन संदर्भ बनाना जो अपवाद को कैप्चर करता है।

public class AsyncSynchronizationContext : SynchronizationContext 
{ 
    public override void Send(SendOrPostCallback d, object state) 
    { 
     try 
     { 
      d(state); 
     } 
     catch (Exception ex) 
     { 
      // Put your exception handling logic here. 

      Console.WriteLine(ex.Message); 
     } 
    } 

    public override void Post(SendOrPostCallback d, object state) 
    { 
     try 
     { 
      d(state); 
     } 
     catch (Exception ex) 
     { 
      // Put your exception handling logic here. 

      Console.WriteLine(ex.Message); 
     } 
    } 
} 

आप ऊपर catch में अपने अपवाद हैंडलिंग तर्क रख सकते हैं:

यहां मुद्दा यह है कि तुल्यकालन संदर्भ पदों थ्रेड पूल के लिए कॉलबैक, लेकिन यह चारों ओर आज़माएं/कैच के साथ है।

इसके बाद, हर धागा (SynchronizationContext.Current[ThreadStatic] है) जहाँ आप इस तंत्र के साथ async तरीकों पर अमल करना चाहते हैं पर, आप वर्तमान समन्वयन संदर्भ सेट करना होगा:

SynchronizationContext.SetSynchronizationContext(new AsyncSynchronizationContext()); 

पूरा Main उदाहरण:

class Program 
{ 
    static void Main(string[] args) 
    { 
     SynchronizationContext.SetSynchronizationContext(new AsyncSynchronizationContext()); 

     ExecuteAsyncMethod(); 

     Console.ReadKey(); 
    } 

    private static async void ExecuteAsyncMethod() 
    { 
     await AsyncMethod(); 
    } 

    private static async Task AsyncMethod() 
    { 
     throw new Exception("Exception from async"); 
    } 
} 
+3

मुझे खेद है, लेकिन मुझे नहीं पता कि थ्रेड पूल पर आपका सिंक्रनाइज़ेशन कॉन्टेक्स्ट कुछ भी कहां पोस्ट करता है - जहां तक ​​मैं इसे देख सकता हूं, यह कॉलिंग थ्रेड और रिटर्न पर तुरंत फ़ंक्शन निष्पादित करता है। –

18

मैं एप्लिकेशन को नीचे लाने से एसिंक विधि से फेंकने वाले अपवादों को कैसे रोक सकता हूं?

इन सर्वोत्तम प्रथाओं का पालन करें:

  1. सभी async तरीकों लौटना चाहिए Task या Task<T> जब तक कि वे void (जैसे, ईवेंट हैंडलर्स) वापस जाने के लिए है।
  2. किसी बिंदु पर, आपको await सभी Taskasync विधियों से वापस लौटाया जाना चाहिए। एकमात्र कारण यह है कि आप ऐसा नहीं करना चाहते हैं यदि आप अब ऑपरेशन के नतीजे की परवाह नहीं करते हैं (उदाहरण के लिए, इसे रद्द करने के बाद)।
  3. यदि आपको async void ईवेंट हैंडलर से अपवाद पकड़ने की आवश्यकता है, तो इसे ईवेंट हैंडलर में पकड़ें - ठीक उसी तरह जैसे आप सिंक्रोनस कोड करते हैं।

आप मेरी async/await intro post सहायक पा सकते हैं; मैं वहां कई अन्य सर्वोत्तम प्रथाओं को भी कवर करता हूं।

+1

मैं समझता हूं कि मुझे जो समस्या आ रही है वह यह है कि मेरे पास 'void'' async' विधियां हैं। हालांकि, क्या इसका मतलब यह है कि जिस मुद्दे का मैं वर्णन कर रहा हूं वह ईवेंट हैंडलर के लिए होता है? –

+0

हां; तीसरे बिंदु को दोहराने के लिए, यदि आपके पास 'async void' ईवेंट हैंडलर है, तो आप शायद उस ईवेंट हैंडलर * के भीतर अपवाद * पकड़ना चाहते हैं। एक सिंक्रोनस इवेंट हैंडलर में एक ही समस्या होगी - अगर यह थ्रेड पूल थ्रेड पर चल रहा है तो अपवाद बच निकलेगा तो यह ऐप को क्रैश करेगा। –

+0

सिवाय इसके कि अपवाद को 'सिंक्रनाइज़ेशन कॉन्टेक्स्ट' पर भेजा जाता है जिसे 'async' विधि लागू होने पर कैद किया गया था। WinForms/WPF अनुप्रयोग में अधिकांश समय यह सही सिंक्रनाइज़ेशन संदर्भ होगा क्योंकि ईवेंट UI UI थ्रेड से शुरू हो जाएगा। ऐसा लगता है कि व्यवहार ऐसा नहीं होता है जब घटना 'async' नहीं होती है। –