असिंक्रोनस प्रोग्रामिंग कोड बेस के माध्यम से "बढ़ता" है। इसकी तुलना एक ज़ोंबी वायरस से की गई है । सबसे अच्छा समाधान यह है कि इसे बढ़ने दें, लेकिन कभी-कभी यह संभव नहीं है।
मैंने आंशिक रूप से अतुल्यकालिक कोड बेस से निपटने के लिए अपने Nito.AsyncEx लाइब्रेरी में कुछ प्रकार लिखे हैं । कोई समाधान नहीं है जो हर स्थिति में काम करता है, हालांकि।
समाधान ए
यदि आपके पास एक सरल अतुल्यकालिक विधि है जिसे इसके संदर्भ में वापस सिंक्रनाइज़ करने की आवश्यकता नहीं है, तो आप उपयोग कर सकते हैं Task.WaitAndUnwrapException:
var task = MyAsyncMethod();
var result = task.WaitAndUnwrapException();
आप उपयोग नहीं करना चाहते हैं Task.Waitया Task.Resultक्योंकि वे अपवादों को लपेटते हैं AggregateException।
यह समाधान केवल तभी उपयुक्त है जब MyAsyncMethodइसके संदर्भ में वापस सिंक्रनाइज़ नहीं किया जाता है। दूसरे शब्दों में, हर awaitमें MyAsyncMethodसाथ समाप्त होना चाहिए ConfigureAwait(false)। इसका मतलब यह है कि यह किसी भी यूआई तत्वों को अद्यतन नहीं कर सकता है या ASP.NET अनुरोध संदर्भ तक नहीं पहुंच सकता है।
समाधान बी
यदि MyAsyncMethodआपको इसके संदर्भ में वापस सिंक्रनाइज़ करने की आवश्यकता है, तो आप AsyncContext.RunTaskएक नेस्टेड संदर्भ प्रदान करने के लिए उपयोग करने में सक्षम हो सकते हैं :
var result = AsyncContext.RunTask(MyAsyncMethod).Result;
* अद्यतन 4/14/2014: पुस्तकालय के हाल के संस्करणों में एपीआई इस प्रकार है:
var result = AsyncContext.Run(MyAsyncMethod);
( Task.Resultइस उदाहरण में उपयोग करना ठीक है क्योंकि अपवादों का RunTaskप्रचार होगा Task)।
इसके AsyncContext.RunTaskबजाय आपको इसकी आवश्यकता हो सकती Task.WaitAndUnwrapExceptionहै क्योंकि WinForms / WPF / SL / ASP.NET पर होने वाले एक सूक्ष्म गतिरोध की संभावना के कारण है:
- एक तुल्यकालिक विधि एक async विधि को बुलाती है, एक प्राप्त करना
Task।
- सिंक्रोनस विधि पर एक अवरुद्ध प्रतीक्षा करता है
Task।
asyncविधि का उपयोग करता awaitबिना ConfigureAwait।
Taskइस स्थिति में पूरा नहीं कर सकते क्योंकि यह केवल पूरा करता है जब asyncविधि समाप्त हो गया है; यह asyncविधि पूर्ण नहीं हो सकती क्योंकि यह इसके निरंतरता को शेड्यूल करने का प्रयास कर रहा है SynchronizationContext, और WinForms / WPF / SL / ASP.NET इस निरंतरता को चलाने की अनुमति नहीं देगा क्योंकि सिंक्रोनस विधि पहले से ही उस संदर्भ में चल रही है।
यह एक कारण है कि जितना संभव हो ConfigureAwait(false)हर asyncविधि के भीतर उपयोग करना एक अच्छा विचार है।
समाधान सी
AsyncContext.RunTaskहर परिदृश्य में काम नहीं करेगा। उदाहरण के लिए, यदि asyncविधि ऐसी चीज़ का इंतजार करती है जिसके लिए UI ईवेंट को पूरा करने की आवश्यकता होती है, तो आप नेस्टेड संदर्भ के साथ भी गतिरोध करेंगे। उस स्थिति में, आप asyncथ्रेड पूल पर विधि शुरू कर सकते हैं :
var task = Task.Run(async () => await MyAsyncMethod());
var result = task.WaitAndUnwrapException();
हालाँकि, इस समाधान के MyAsyncMethodलिए थ्रेड पूल संदर्भ में काम करना होगा। इसलिए यह UI तत्वों को अपडेट नहीं कर सकता है या ASP.NET अनुरोध संदर्भ तक नहीं पहुंच सकता है। और उस स्थिति में, आप ConfigureAwait(false)इसके awaitकथनों में जोड़ सकते हैं , और समाधान ए का उपयोग कर सकते हैं।
अपडेट, 2019-05-01: वर्तमान "सबसे कम-सबसे खराब प्रथाएं" यहां एक एमएसडीएन लेख में हैं ।