अपवाद प्रचार में एक बड़ा अंतर है । एक अपवाद, एक अंदर फेंक दिया async Taskविधि, लौटे में संग्रहीत हो जाता है Taskवस्तु और निष्क्रिय बनी हुई है जब तक काम के माध्यम से मनाया जाता है await task, task.Wait(), task.Resultया task.GetAwaiter().GetResult()। विधि के समकालिक भाग से फेंके जाने पर भी यह इस तरह से प्रचारित होता है async।
निम्नलिखित कोड पर विचार करें, जहां OneTestAsyncऔर AnotherTestAsyncअलग तरह से व्यवहार करते हैं:
static async Task OneTestAsync(int n)
{
await Task.Delay(n);
}
static Task AnotherTestAsync(int n)
{
return Task.Delay(n);
}
static void DoTestAsync(Func<int, Task> whatTest, int n)
{
Task task = null;
try
{
task = whatTest(n);
Console.Write("Press enter to continue");
Console.ReadLine();
task.Wait();
}
catch (Exception ex)
{
Console.Write("Error: " + ex.Message);
}
}
अगर मैं फोन DoTestAsync(OneTestAsync, -2)करता हूं , तो यह निम्नलिखित आउटपुट का उत्पादन करता है:
जारी रखने के लिए एंटर दबाएं
त्रुटि: एक या अधिक त्रुटियां हुईं
त्रुटि: २
ध्यान दें, मुझे Enterइसे देखने के लिए प्रेस करना था ।
अब, अगर मैं कॉल करता हूं, तो DoTestAsync(AnotherTestAsync, -2)अंदर कोड वर्कफ़्लो DoTestAsyncकाफी अलग है, और इसलिए आउटपुट है। इस बार, मुझे प्रेस करने के लिए नहीं कहा गया था Enter:
त्रुटि: मान या तो -1 होना चाहिए (एक अनंत टाइमआउट को दर्शाता है), 0 या एक सकारात्मक पूर्णांक।
पैरामीटर नाम: मिलीसेकंडडेलएयर: 1
दोनों मामलों में Task.Delay(-2)अपने मापदंडों को मान्य करते हुए, शुरुआत में फेंकता है। यह एक बना-बनाया परिदृश्य हो सकता है, लेकिन सिद्धांत में Task.Delay(1000), उदाहरण के लिए, अंतर्निहित सिस्टम टाइमर API के विफल होने पर भी फेंक सकता है।
साइड नोट पर, त्रुटि प्रसार तर्क async voidविधियों के लिए अभी तक भिन्न है (जैसा कि async Taskविधियों के विपरीत )। किसी async voidविधि के अंदर उठाया गया अपवाद वर्तमान थ्रेड के सिंक्रनाइज़ेशन संदर्भ (थ्रू SynchronizationContext.Post) पर तुरंत फिर से फेंक दिया जाएगा , यदि वर्तमान थ्रेड में एक है ( SynchronizationContext.Current != null)अन्यथा, अन्यथा, इसे फिर से फेंक दिया जाएगा ThreadPool.QueueUserWorkItem)। कॉलर के पास एक ही स्टैक फ्रेम पर इस अपवाद को संभालने का मौका नहीं है।
मैंने यहाँ और यहाँ TPL अपवाद हैंडलिंग व्यवहार के बारे में कुछ और विवरण पोस्ट किए हैं ।
प्रश्न : क्या asyncगैर-एसिंक्स- Taskआधारित विधियों के लिए तरीकों के अपवाद प्रसार व्यवहार की नकल करना संभव है , ताकि बाद वाला समान स्टैक फ्रेम पर न फेंके?
एक : अगर वास्तव में जरूरत है, तो हाँ, इसके लिए एक चाल है:
async Task<int> MethodAsync(int arg)
{
if (arg < 0)
throw new ArgumentException("arg");
return 42 + arg;
}
Task<int> MethodAsync(int arg)
{
var task = new Task<int>(() =>
{
if (arg < 0)
throw new ArgumentException("arg");
return 42 + arg;
});
task.RunSynchronously(TaskScheduler.Default);
return task;
}
हालाँकि, कुछ शर्तों के तहत ध्यान दें (जैसे कि यह स्टैक पर बहुत गहरा है), RunSynchronouslyफिर भी अतुल्यकालिक रूप से निष्पादित कर सकता है।
एक और उल्लेखनीय अंतर यह है कि है
/ संस्करण अधिक मृत-लॉक एक गैर-डिफ़ॉल्ट तुल्यकालन संदर्भ पर की संभावना है । उदाहरण के लिए, WinForms या WPF एप्लिकेशन में निम्नलिखित डेड-लॉक होगा:
asyncawait
static async Task TestAsync()
{
await Task.Delay(1000);
}
void Form_Load(object sender, EventArgs e)
{
TestAsync().Wait();
}
इसे नॉन-एस्किंट वर्जन में बदलें और यह डेड-लॉक नहीं होगा:
Task TestAsync()
{
return Task.Delay(1000);
}
मृत-लॉक की प्रकृति को स्टीफन क्लीरी ने अपने ब्लॉग में अच्छी तरह से समझाया है ।
await/async:) :)