अपवाद प्रचार में एक बड़ा अंतर है । एक अपवाद, एक अंदर फेंक दिया 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 एप्लिकेशन में निम्नलिखित डेड-लॉक होगा:
async
await
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
:) :)