सुरक्षित रूप से बिना प्रतीक्षा के C # में एक async विधि कैसे कॉल करें


322

मेरे पास एक asyncतरीका है जो कोई डेटा नहीं लौटाता है:

public async Task MyAsyncMethod()
{
    // do some stuff async, don't return any data
}

मैं इसे दूसरी विधि से कॉल कर रहा हूं जो कुछ डेटा लौटाता है:

public string GetStringData()
{
    MyAsyncMethod(); // this generates a warning and swallows exceptions
    return "hello world";
}

कॉलिंग MyAsyncMethod()का इंतजार कर रहा है यह एक "का कारण बनता है बिना क्योंकि इस कॉल की प्रतीक्षा नहीं है, वर्तमान विधि कॉल पूर्ण होने से पहले चलाने के लिए जारी है दृश्य स्टूडियो में चेतावनी"। उस चेतावनी के पेज पर यह लिखा है:

आपको चेतावनी को दबाने पर विचार करना चाहिए, यदि आप सुनिश्चित हैं कि आप अतुल्यकालिक कॉल को पूरा करने के लिए इंतजार नहीं करना चाहते हैं और कहा जाता है कि कोई भी अपवाद नहीं उठाया जाएगा

मुझे यकीन है कि मैं कॉल के पूरा होने का इंतजार नहीं करना चाहता; मुझे या करने की आवश्यकता नहीं है। लेकिन कॉल अपवादों को बढ़ा सकता है।

मैं इस समस्या में कुछ बार ठोकर खा चुका हूँ और मुझे यकीन है कि यह एक सामान्य समस्या है जिसका एक सामान्य समाधान होना चाहिए।

मैं परिणाम की प्रतीक्षा किए बिना सुरक्षित रूप से एक async विधि कैसे कहूं?

अपडेट करें:

लोगों को यह सुझाव देने के लिए कि मुझे बस परिणाम का इंतजार है, यह कोड है जो हमारी वेब सेवा (ASP.NET वेब एपीआई) पर एक वेब अनुरोध का जवाब दे रहा है। UI संदर्भ में प्रतीक्षा करने से UI थ्रेड मुक्त रहता है, लेकिन वेब अनुरोध कॉल का इंतजार अनुरोध का जवाब देने से पहले कार्य समाप्त होने तक प्रतीक्षा करेगा, जिससे बिना किसी कारण के प्रतिक्रिया समय बढ़ेगा।


सिर्फ एक पूरा करने का तरीका क्यों नहीं बनाया और सिर्फ इसे अनदेखा किया? क्योंकि अगर यह बैकग्राउंड थ्रेड पर चल रहा है। फिर यह आपके कार्यक्रम को वैसे भी समाप्त करने से नहीं रोकेगा।
याह्या

1
यदि आप परिणाम के लिए इंतजार नहीं करना चाहते हैं, तो चेतावनी को नजरअंदाज / दबाने के लिए एकमात्र विकल्प है। यदि आप परिणाम / अपवाद की प्रतीक्षा करना चाहते हैं तोMyAsyncMethod().Wait()
पीटर रिची

1
आपके संपादन के बारे में: इससे मुझे कोई मतलब नहीं है। कहें कि रिक्वेस्ट के बाद क्लाइंट को 1 सेकंड में रिस्पॉन्स भेजा जाता है, और 2 सेकेंड बाद में आपका एसिंक्स मेथड एक अपवाद फेंकता है। आप उस अपवाद के साथ क्या करेंगे? यदि आप अपनी प्रतिक्रिया पहले ही भेज चुके हैं, तो आप इसे ग्राहक को नहीं भेज सकते। आप इसके साथ और क्या करेंगे?

2
@ रोमोकू मेला काफी यह मानते हुए कि कोई व्यक्ति लॉग देखता है, वैसे भी। :)

2
ASP.NET वेब एपीआई परिदृश्य पर एक भिन्नता एक लंबे समय से चली आ रही प्रक्रिया में एक स्व-होस्टेड वेब एपीआई है (जैसे, कहते हैं, एक विंडोज सेवा), जहां एक अनुरोध कुछ महंगा करने के लिए एक लंबी पृष्ठभूमि का कार्य बनाता है, लेकिन फिर भी वह चाहता है HTTP 202 (स्वीकृत) के साथ तेज़ी से प्रतिक्रिया प्राप्त करें।
डेविड रूबिन

जवाबों:


186

यदि आप "अतुल्यकालिक" अपवाद प्राप्त करना चाहते हैं, तो आप कर सकते हैं:

  MyAsyncMethod().
    ContinueWith(t => Console.WriteLine(t.Exception),
        TaskContinuationOptions.OnlyOnFaulted);

यह आपको "मुख्य" धागे के अलावा एक धागे पर एक अपवाद से निपटने की अनुमति देगा। इसका मतलब है कि आपको कॉल करने MyAsyncMethod()वाले धागे से कॉल के लिए "इंतजार" नहीं करना है MyAsyncMethod; लेकिन, फिर भी आपको अपवाद के साथ कुछ करने की अनुमति देता है - लेकिन केवल अगर कोई अपवाद होता है।

अपडेट करें:

तकनीकी रूप से, आप ऐसा ही कुछ कर सकते हैं await:

try
{
    await MyAsyncMethod().ConfigureAwait(false);
}
catch (Exception ex)
{
    Trace.WriteLine(ex);
}

... जो उपयोगी होगा यदि आपको विशेष रूप से try/ catch(या using) का उपयोग करने की आवश्यकता है, लेकिन मुझे लगता है कि ContinueWithयह थोड़ा अधिक स्पष्ट है क्योंकि आपको यह जानना होगा कि ConfigureAwait(false)इसका क्या मतलब है।


7
मैंने इसे एक एक्सटेंशन विधि में बदल दिया Task: सार्वजनिक स्थैतिक वर्ग AsyncUtility {सार्वजनिक स्थैतिक शून्य PerformAsyncTaskWithoutAwait (यह कार्य टास्क, कार्य <टास्क> अपवादHandler) {var dummy = task.ContinueWith (t => अपवादHandler (t), TaskContinuationOptions.OnlyOnLault); }} उपयोग: MyAsyncMethod ()। PerformAsyncTaskWithoutAwait (t => log.ErrorFormat ("MyAsyncMethod: \ n {0}", t.Exception) को कॉल करते समय एक त्रुटि हुई;
मार्क एवेनियस

2
downvoter। टिप्पणियाँ? यदि उत्तर में कुछ गलत है, तो मुझे जानना और / या ठीक करना अच्छा लगेगा।
पीटर रिची

2
अरे - मैंने नीचा नहीं देखा, लेकिन ... क्या आप अपने अपडेट की व्याख्या कर सकते हैं? कॉल का इंतजार नहीं किया जाना चाहिए था, इसलिए यदि आप इसे पसंद करते हैं, तो आप कॉल के लिए इंतजार करते हैं, आप बस कैप्चर किए गए संदर्भ पर जारी नहीं रखते ...
बार्टोज़

1
"प्रतीक्षा" इस संदर्भ में सही विवरण नहीं है। ConfiguratAwait(false)कार्य पूरा होने तक निष्पादित नहीं होने के बाद की रेखा , लेकिन वर्तमान थ्रेड उसके लिए "प्रतीक्षा" (यानी ब्लॉक) नहीं करता है, अगली पंक्ति को एसिंक्रोनस रूप से प्रतीक्षित आह्वान के रूप में कहा जाता है। इसके बिना ConfigureAwait(false)मूल वेब अनुरोध संदर्भ पर अगली पंक्ति निष्पादित की जाएगी। साथ ConfigurateAwait(false)यह async विधि (कार्य) के रूप में एक ही संदर्भ में मार डाला है, पर जारी रखने के लिए मूल संदर्भ / धागा मुक्त कराने ...
पीटर रिची

15
कंटिन्यू वर्थ संस्करण कोशिश {{पकड़} {} संस्करण के समान नहीं है। पहले संस्करण में, ContinueWith () के बाद सब कुछ तुरंत निष्पादित होगा। प्रारंभिक कार्य को निकाल दिया जाता है और भुला दिया जाता है। दूसरे संस्करण में, {} को पकड़ने के बाद सब कुछ प्रारंभिक कार्य पूरा होने के बाद ही निष्पादित होगा। दूसरे संस्करण "इंतजार MyAsyncMethod () के बराबर है ContinueWith (टी => Console.WriteLine (t.Exception), TaskContinuationOptions.OnlyOnFaulted) .ConfigureAwait (fals);।
Thanasis Ioannidis

67

आपको पहले GetStringDataएक asyncविधि बनाने पर विचार करना चाहिए और यह awaitकार्य जिस पर से लौटा है MyAsyncMethod

यदि आप पूरी तरह से आश्वस्त हैं कि आपको अपवादों को संभालने की जरूरत नहीं है MyAsyncMethod या जब यह पूरा हो जाएगा, तब आप ऐसा कर सकते हैं:

public string GetStringData()
{
  var _ = MyAsyncMethod();
  return "hello world";
}

BTW, यह एक "आम समस्या" नहीं है। यह बहुत दुर्लभ है कि कुछ कोड निष्पादित करना चाहते हैं और परवाह नहीं है कि क्या यह पूरा हो गया है और परवाह नहीं है कि क्या यह सफलतापूर्वक पूरा होता है।

अपडेट करें:

चूंकि आप ASP.NET पर हैं और जल्दी लौटना चाहते हैं, इसलिए आपको मेरा ब्लॉग पोस्ट उपयोगी हो सकता है । हालाँकि, ASP.NET को इसके लिए डिज़ाइन नहीं किया गया था, और इस बात की कोई गारंटी नहीं है कि प्रतिक्रिया वापस आने के बाद आपका कोड चलेगा। ASP.NET इसे चलाने की पूरी कोशिश करेगा, लेकिन यह इसकी गारंटी नहीं दे सकता।

तो, यह किसी घटना को लॉग इन करने की तरह सरल कुछ के लिए एक अच्छा समाधान है जहां यह वास्तव में कोई फर्क नहीं पड़ता अगर आप यहां और वहां कुछ खो देते हैं। यह किसी भी तरह के व्यवसाय-महत्वपूर्ण संचालन के लिए एक अच्छा समाधान नहीं है। उन स्थितियों में, आपको एक अधिक जटिल आर्किटेक्चर अपनाना चाहिए , ताकि उन्हें बचाने के लिए लगातार तरीके (जैसे, एज़्योर क्युए, एमएसएमक्यू) और एक अलग पृष्ठभूमि प्रक्रिया (जैसे, एज़्योर वर्कर रोल, विन 32 सर्विस) को संसाधित किया जा सके।


2
मुझे लगता है कि आपको गलतफहमी हुई होगी। मैं ऐसा करता है, तो यह अपवाद फेंकता है और विफल रहता देखभाल, लेकिन मैं अपने डेटा लौटने से पहले विधि का इंतजार करने के लिए नहीं करना चाहती। यदि मैं उस संदर्भ में काम कर रहा हूं, जिसमें कोई फर्क पड़ता है, तो मेरे संपादन को भी देखें।
जॉर्ज पॉवेल

5
@GeorgePowell: एक सक्रिय अनुरोध के बिना ASP.NET संदर्भ में कोड चलाना बहुत खतरनाक है। मेरे पास एक ब्लॉग पोस्ट है जो आपकी मदद कर सकती है , लेकिन आपकी अधिक समस्या को जाने बिना मैं यह नहीं कह सकता कि मैं उस दृष्टिकोण की सिफारिश करूंगा या नहीं।
स्टीफन क्लीयर

@StephenCleary मुझे इसी तरह की आवश्यकता है। मेरे उदाहरण में, मुझे क्लाउड में चलने के लिए बैच प्रोसेसिंग इंजन की आवश्यकता है, मैं बैच प्रोसेसिंग को बंद करने के लिए अंतिम बिंदु "पिंग" करूंगा, लेकिन मैं तुरंत वापस लौटना चाहता हूं। पिंगिंग के बाद से यह शुरू हो जाता है, यह वहां से सब कुछ संभाल सकता है। वहाँ अपवाद फेंक दिया जाता है, तो वे सिर्फ मेरी "BatchProcessLog / त्रुटि" तालिकाओं में लॉग इन होना चाहता हूँ ...
Ganders

8
सी # 7 में, आप बदल सकते हैं var _ = MyAsyncMethod();के साथ _ = MyAsyncMethod();। यह अभी भी CS4014 की चेतावनी से बचा जाता है, लेकिन यह थोड़ा अधिक स्पष्ट करता है कि आप चर का उपयोग नहीं कर रहे हैं।
ब्रायन

48

पीटर रिची का जवाब जो मैं चाहता था, और ASP.NET में जल्दी लौटने के बारे में स्टीफन क्लीरी का लेख बहुत मददगार था।

हालाँकि, एक सामान्य समस्या के रूप में (ASP.NET संदर्भ के लिए विशिष्ट नहीं) निम्नलिखित कंसोल एप्लिकेशन उपयोग करके पीटर के उत्तर के उपयोग और व्यवहार को दर्शाता है। Task.ContinueWith(...)

static void Main(string[] args)
{
  try
  {
    // output "hello world" as method returns early
    Console.WriteLine(GetStringData());
  }
  catch
  {
    // Exception is NOT caught here
  }
  Console.ReadLine();
}

public static string GetStringData()
{
  MyAsyncMethod().ContinueWith(OnMyAsyncMethodFailed, TaskContinuationOptions.OnlyOnFaulted);
  return "hello world";
}

public static async Task MyAsyncMethod()
{
  await Task.Run(() => { throw new Exception("thrown on background thread"); });
}

public static void OnMyAsyncMethodFailed(Task task)
{
  Exception ex = task.Exception;
  // Deal with exceptions here however you want
}

GetStringData()जल्दी का इंतजार कर के बिना रिटर्न MyAsyncMethod()और अपवादों में फेंक दिया MyAsyncMethod()में निपटाया जाता है OnMyAsyncMethodFailed(Task task)और नहीं में try/ catchचारों ओरGetStringData()


9
निकालें Console.ReadLine();और थोड़ी नींद / देरी जोड़ें MyAsyncMethodऔर आप कभी भी अपवाद नहीं देखेंगे।
22

17

मैं इस समाधान के साथ समाप्त होता हूं:

public async Task MyAsyncMethod()
{
    // do some stuff async, don't return any data
}

public string GetStringData()
{
    // Run async, no warning, exception are catched
    RunAsync(MyAsyncMethod()); 
    return "hello world";
}

private void RunAsync(Task task)
{
    task.ContinueWith(t =>
    {
        ILog log = ServiceLocator.Current.GetInstance<ILog>();
        log.Error("Unexpected Error", t.Exception);

    }, TaskContinuationOptions.OnlyOnFaulted);
}

8

इसे आग और भूल कहा जाता है, और उसके लिए एक विस्तार है।

एक कार्य का उपभोग करता है और इसके साथ कुछ भी नहीं करता है। आग और भूलने के लिए उपयोगी async विधियों के भीतर async विधियों के लिए कॉल।

नगेट पैकेज स्थापित करें ।

उपयोग:

MyAsyncMethod().Forget();

12
यह कुछ भी नहीं करता है, यह चेतावनी को हटाने के लिए सिर्फ एक चाल है। देखें github.com/microsoft/vs-threading/blob/master/src/...
पाओलो Fulgoni

2

मुझे लगता है कि सवाल उठता है, आपको ऐसा करने की आवश्यकता क्यों होगी? के लिए कारण asyncतो आप एक परिणाम का इंतजार कर सकते हैं सी # में 5.0 है। यह विधि वास्तव में अतुल्यकालिक नहीं है, लेकिन बस एक समय में बुलाया जाता है ताकि वर्तमान धागे के साथ बहुत अधिक हस्तक्षेप न करें।

शायद एक धागा शुरू करने और इसे अपने दम पर खत्म करने के लिए छोड़ना बेहतर हो सकता है।


2
asyncएक परिणाम की तुलना में "प्रतीक्षा कर रहा है" की तुलना में थोड़ा अधिक है। "वेट" का तात्पर्य है कि "वेट" के बाद की पंक्तियों को "प्रतीक्षा" के लिए लागू एक ही धागे पर एसिंक्रोनस रूप से निष्पादित किया जाता है। यह "प्रतीक्षा" के बिना किया जा सकता है, ज़ाहिर है, लेकिन आप अंत में प्रतिनिधियों का एक समूह बनाते हैं usingऔर कोड के अनुक्रमिक रूप-और-साथ ही उपयोग करने की क्षमता खो देते हैं और try/catch...
पीटर रिची

1
@PeterRitchie आपके पास एक ऐसी विधि हो सकती है जो कार्यात्मक रूप से अतुल्यकालिक है, awaitकीवर्ड का उपयोग नहीं करती है , और कीवर्ड का उपयोग नहीं करती है async, लेकिन उस पद्धति की परिभाषा का asyncउपयोग किए बिना कीवर्ड का उपयोग करने में कोई मतलब नहीं है await
सर्व करें

1
@PeterRitchie इस कथन से: " asyncपरिणाम की प्रतीक्षा में" बस थोड़ा सा अधिक है। asyncकीवर्ड (आप गर्भित यह बैकटिक में यह बंद करके कीवर्ड है) का अर्थ है कुछ भी नहीं परिणाम का इंतजार कर रहा से अधिक है। यह एसिंक्रोनसी है, सामान्य सीएस अवधारणाओं के रूप में, इसका मतलब है कि परिणाम का इंतजार करने की तुलना में अधिक है।
Servy

1
@ Servy asyncएक ऐसी राज्य मशीन बनाता है जो किसी भी प्रतीक्षारत को async विधि के तहत प्रबंधित करती है। यदि awaitविधि के भीतर कोई एस नहीं हैं, तो यह अभी भी उस राज्य मशीन को बनाता है - लेकिन विधि अतुल्यकालिक नहीं है। और अगर asyncविधि वापस आती है void, तो प्रतीक्षा करने के लिए कुछ भी नहीं है। इसलिए, यह केवल परिणाम की प्रतीक्षा करने से अधिक है।
पीटर रिची

1
@PeterRitchie जब तक विधि किसी कार्य को वापस करती है तब तक आप उसका इंतजार कर सकते हैं। राज्य मशीन, या asyncकीवर्ड की कोई आवश्यकता नहीं है । आपको केवल इतना करना होगा (और वह सब जो वास्तव में उस विशेष मामले में एक स्टेट मशीन के साथ होता है) यह है कि विधि को समकालिक रूप से चलाया जाता है और फिर एक पूर्ण कार्य में लपेटा जाता है। मुझे लगता है कि तकनीकी रूप से आप सिर्फ राज्य मशीन को नहीं हटाते हैं; आप राज्य मशीन को हटा दें और फिर कॉल करें Task.FromResult। मैंने आपको (और संकलक लेखकों को भी) अपने ऊपर परिशिष्ट जोड़ दिया।
सर्व

0

संदेश लूप वाली तकनीकों पर (यदि एएसपी उनमें से एक है तो निश्चित नहीं), आप कार्य समाप्त होने तक लूप और प्रक्रिया संदेशों को ब्लॉक कर सकते हैं, और कोड को अनब्लॉक करने के लिए ContinueWith का उपयोग करें:

public void WaitForTask(Task task)
{
    DispatcherFrame frame = new DispatcherFrame();
    task.ContinueWith(t => frame.Continue = false));
    Dispatcher.PushFrame(frame);
}

यह दृष्टिकोण ShowDialog पर अवरुद्ध होने और अभी भी UI को उत्तरदायी रखने के समान है।


0

मुझे यहां पार्टी करने में देर हो गई है, लेकिन एक भयानक पुस्तकालय है जिसका मैं उपयोग कर रहा हूं जिसे मैंने अन्य उत्तरों में संदर्भित नहीं देखा है

https://github.com/brminnick/AsyncAwaitBestPractices

यदि आपको "फायर एंड फ़ॉरगेट" करने की आवश्यकता है, तो आप कार्य पर विस्तार विधि कहते हैं।

कॉल के लिए एक्शन अपवाद पर पासिंग सुनिश्चित करता है कि आपको दोनों दुनिया का सबसे अच्छा मिलता है - निष्पादन की प्रतीक्षा करने और अपने उपयोगकर्ताओं को धीमा करने की आवश्यकता नहीं है, जबकि एक सुंदर तरीके से अपवाद को संभालने की क्षमता बनाए रखते हुए।

अपने उदाहरण में आप इसे इस तरह उपयोग करेंगे:

   public string GetStringData()
    {
        MyAsyncMethod().SafeFireAndForget(onException: (exception) =>
                    {
                      //DO STUFF WITH THE EXCEPTION                    
                    }); 
        return "hello world";
    }

यह भी प्रतीक्षायोग्य AsyncCommands ICommand को लागू करता है जो मेरे MVVM Xamarin समाधान के लिए महान है


-1

समाधान Sincronization संदर्भ के बिना एक और निष्पादन कार्य में HttpClient शुरू कर रहा है:

var submit = httpClient.PostAsync(uri, new StringContent(body, Encoding.UTF8,"application/json"));
var t = Task.Run(() => submit.ConfigureAwait(false));
await t.ConfigureAwait(false);

-1

यदि आप वास्तव में ऐसा करना चाहते हैं। बस "बिना किसी प्रतीक्षा के C # में एक async विधि को कॉल करने के लिए", आप async विधि को एक के अंदर निष्पादित कर सकते हैं Task.Run। यह दृष्टिकोण MyAsyncMethodसमाप्त होने तक प्रतीक्षा करेगा ।

public string GetStringData()
{
    Task.Run(()=> MyAsyncMethod()).Result;
    return "hello world";
}

awaitअतुल्यकालिक रूप Resultसे आपके कार्य के अलिखित हैं , जबकि परिणाम का उपयोग तब तक अवरुद्ध रहेगा जब तक कि कार्य पूरा नहीं हो जाता।


-1

आमतौर पर async विधि टास्क क्लास देता है। यदि आप Wait()विधि या Resultसंपत्ति का उपयोग करते हैं और कोड अपवाद को छोड़ देता है - अपवाद प्रकार में लिपट जाता है AggregateException- तो आपको Exception.InnerExceptionसही अपवाद का पता लगाने के लिए क्वेरी करने की आवश्यकता है।

लेकिन इसके .GetAwaiter().GetResult()बजाय इसका उपयोग करना भी संभव है - यह async कार्य की प्रतीक्षा भी करेगा, लेकिन अपवाद नहीं लपेटेगा।

तो यहाँ संक्षिप्त उदाहरण है:

public async Task MyMethodAsync()
{
}

public string GetStringData()
{
    MyMethodAsync().GetAwaiter().GetResult();
    return "test";
}

तुम भी async फ़ंक्शन से कुछ पैरामीटर वापस करने में सक्षम होना चाहते हो सकता है - जो कि Action<return type>async फ़ंक्शन में अतिरिक्त प्रदान करके प्राप्त किया जा सकता है , जैसे:

public string GetStringData()
{
    return MyMethodWithReturnParameterAsync().GetAwaiter().GetResult();
}

public async Task<String> MyMethodWithReturnParameterAsync()
{
    return "test";
}

कृपया ध्यान दें कि async विधियों में आमतौर पर ASyncप्रत्यय नामकरण होता है, बस एक ही नाम के साथ सिंक फ़ंक्शंस के बीच टकराव से बचने में सक्षम होने के लिए। (जैसे FileStream.ReadAsync) - मैंने इस सिफारिश का पालन करने के लिए फ़ंक्शन नाम अपडेट किए हैं।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.