इस कॉल की चेतावनी का इंतजार नहीं किया जा रहा है, वर्तमान पद्धति का निष्पादन जारी है


135

बस VS2012 मिल गया और एक संभाल पाने की कोशिश कर रहा है async

मान लीजिए कि मुझे एक विधि मिली है जो एक अवरुद्ध स्रोत से कुछ मूल्य प्राप्त करती है। मुझे ब्लॉक करने की विधि का कॉलर नहीं चाहिए। मैं कॉलबैक लेने के लिए विधि लिख सकता हूं, जो मान आने पर आह्वान किया जाता है, लेकिन जब से मैं C # 5 का उपयोग कर रहा हूं, मैं इस पद्धति को बनाने का निर्णय लेता हूं ताकि कॉल करने वालों को कॉलबैक से निपटने की आवश्यकता न हो:

// contrived example (edited in response to Servy's comment)
public static Task<string> PromptForStringAsync(string prompt)
{
    return Task.Factory.StartNew(() => {
        Console.Write(prompt);
        return Console.ReadLine();
    });
}

यहां एक उदाहरण विधि है जो इसे कॉल करती है। यदि PromptForStringAsyncयह नहीं था, तो इस पद्धति में कॉलबैक के लिए कॉलबैक की आवश्यकता होगी। Async के साथ, मुझे अपनी विधि को बहुत ही स्वाभाविक तरीके से लिखना है:

public static async Task GetNameAsync()
{
    string firstname = await PromptForStringAsync("Enter your first name: ");
    Console.WriteLine("Welcome {0}.", firstname);

    string lastname = await PromptForStringAsync("Enter your last name: ");
    Console.WriteLine("Name saved as '{0} {1}'.", firstname, lastname);
}

अब तक सब ठीक है। समस्या यह है कि जब मैं GetNameAsync को कॉल करता हूं:

public static void DoStuff()
{
    GetNameAsync();
    MainWorkOfApplicationIDontWantBlocked();
}

पूरे बिंदु GetNameAsyncयह है कि यह अतुल्यकालिक है। मुझे नहीं चाहिए इसे ब्लॉक , क्योंकि मैं MainWorkOfApplicationIDontWantBlocked ASAP पर वापस जाना चाहता हूं और GetNameAsync को पृष्ठभूमि में अपनी बात करने देता हूं। हालाँकि, इसे इस तरह से बुलाना मुझे GetNameAsyncलाइन पर एक संकलक चेतावनी देता है :

Warning 1   Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

मुझे पूरी तरह से पता है कि "कॉल पूरा होने से पहले मौजूदा पद्धति का निष्पादन जारी है"। वह है अतुल्यकालिक कोड बात है, है ना?

मैं अपने कोड को चेतावनी के बिना संकलित करना पसंद करता हूं, लेकिन यहां "ठीक" करने के लिए कुछ भी नहीं है क्योंकि कोड बिल्कुल वही कर रहा है जो मैं इसे करने का इरादा रखता हूं। की रिटर्न वैल्यू को स्टोर करके मैं चेतावनी से छुटकारा पा सकता हूंGetNameAsync :

public static void DoStuff()
{
    var result = GetNameAsync(); // supress warning
    MainWorkOfApplicationIDontWantBlocked();
}

लेकिन अब मेरे पास सुपरफ्लस कोड है। विज़ुअल स्टूडियो को यह समझ में आता है कि मुझे यह अनावश्यक कोड लिखने के लिए मजबूर किया गया था, क्योंकि यह सामान्य "मूल्य कभी इस्तेमाल नहीं" चेतावनी को दबा देता है।

मैं GetNameAsync को एक ऐसी विधि में लपेट कर चेतावनी से छुटकारा पा सकता हूँ जो कि async नहीं है:

    public static Task GetNameWrapper()
    {
        return GetNameAsync();
    }

लेकिन यह और भी अधिक है ज़रूरत से ज़्यादा कोड। इसलिए मुझे एक अनावश्यक चेतावनी देने के लिए कोड लिखने की ज़रूरत नहीं है।

क्या मेरे यहाँ async के उपयोग के बारे में कुछ गलत है?


2
बीटीडब्ल्यू, जब PromptForStringAsyncआप लागू करते हैं तो आपको जरूरत से ज्यादा काम करना पड़ता है; के परिणाम को वापस करें Task.Factory.StartNew। यह पहले से ही एक कार्य है जिसका मूल्य कंसोल में दर्ज स्ट्रिंग है। परिणाम लौटाने के लिए इसका इंतजार करने की कोई आवश्यकता नहीं है; ऐसा करने से कोई नया मूल्य नहीं जुड़ता है।
सेवई

Wwouldn't इसे और अधिक समझ बनाने के लिए GetNameAsyncपूरा नाम है कि उपयोगकर्ता द्वारा प्रदान किया गया (यानी प्रदान करने के लिए Task<Name>है, न कि सिर्फ एक लौटने से Task? DoStuffऔर या तो उसके बाद उस कार्य को संग्रहीत कर सकती है, awaitयह बाद अन्य विधि, या यहाँ तक कि कार्य है कि अन्य को पारित विधि तो यह कर सकता है awaitया Waitइसे कहीं यह के कार्यान्वयन के अंदर।
Servy

@ सरवी: अगर मैं सिर्फ टास्क वापस करता हूं, तो मुझे एक त्रुटि मिलती है "चूंकि यह एक एसिंक्स विधि है, इसलिए रिटर्न एक्सप्रेशन 'टास्क <string>' के बजाय टाइप 'स्ट्रिंग' का होना चाहिए।"
मिट्टी

1
asyncकीवर्ड निकालें ।
22

13
IMO, यह C # टीम की ओर से चेतावनी के लिए एक खराब विकल्प था। चेतावनी उन चीजों के लिए होनी चाहिए जो लगभग निश्चित रूप से गलत हैं। ऐसे बहुत से मामले हैं जहाँ आप "फायर-एंड-फ़र्स्ट" एक async विधि करना चाहते हैं, और दूसरी बार जहाँ आप वास्तव में इसका इंतजार करना चाहते हैं।
मगसम

जवाबों:


103

यदि आपको वास्तव में परिणाम की आवश्यकता नहीं है, तो आप बस GetNameAsyncलौटने के लिए हस्ताक्षर बदल सकते हैं void:

public static async void GetNameAsync()
{
    ...
}

संबंधित प्रश्न के उत्तर को देखने के लिए विचार करें: शून्य लौटाने और कार्य को लौटाने में क्या अंतर है?

अपडेट करें

यदि आपको परिणाम की आवश्यकता है, तो आप GetNameAsyncरिटर्न में बदल सकते हैं, कह सकते हैं Task<string>:

public static async Task<string> GetNameAsync()
{
    string firstname = await PromptForStringAsync("Enter your first name: ");
    string lastname = await PromptForStringAsync("Enter your last name: ");
    return firstname + lastname;
}

और इसे निम्नानुसार उपयोग करें:

public static void DoStuff()
{
    Task<string> task = GetNameAsync();

    // Set up a continuation BEFORE MainWorkOfApplicationIDontWantBlocked
    Task anotherTask = task.ContinueWith(r => {
            Console.WriteLine(r.Result);
        });

    MainWorkOfApplicationIDontWantBlocked();

    // OR wait for the result AFTER
    string result = task.Result;
}

15
यह केवल तभी होगा जब वह कभी भी परिणाम की परवाह न करे , क्योंकि इस बार परिणाम की जरूरत नहीं है ।
22

3
@ सरवाई, राइट, स्पष्टीकरण के लिए धन्यवाद। लेकिन ओपी का GetNameAsyncकोई मूल्य नहीं है (केवल परिणाम को छोड़कर, निश्चित रूप से)।
निकोले खील

2
सही है, लेकिन किसी कार्य को वापस करके वह यह जान सकता है कि यह सभी एसिंक्रोनस कार्यों को पूरा करने के बाद कब समाप्त होगा। यदि यह वापस आता है void, तो उसके पास यह जानने का कोई तरीका नहीं है कि यह कब किया गया है। यही मेरा मतलब है जब मैंने अपनी पिछली टिप्पणी में "परिणाम" कहा था।
सेवई

29
एक सामान्य नियम के रूप में, आपके पास async voidइवेंट हैंडलर को छोड़कर कभी कोई विधि नहीं होनी चाहिए ।
डैनियल मान

2
यहां ध्यान देने वाली एक और बात यह है कि जब आप async voidकिसी अपवाद को स्विच नहीं करते हैं, तो यह व्यवहार भिन्न होता है जब आप किसी भी अपवाद को नहीं पकड़ते हैं जो आपकी प्रक्रिया को क्रैश कर देगा, लेकिन .net 4.5 में यह चलता रहेगा।
कालेब वियर

62

मुझे इस चर्चा में काफी देर हो गई है, लेकिन #pragmaप्री-प्रोसेसर निर्देश का उपयोग करने का विकल्प भी है । मेरे पास यहां और वहां कुछ async कोड हैं जिन्हें मैं स्पष्ट रूप से कुछ स्थितियों में इंतजार नहीं करना चाहता हूं, और मैं आपको बाकी लोगों की तरह चेतावनी और अप्रयुक्त चर नापसंद करता हूं:

#pragma warning disable 4014
SomeMethodAsync();
#pragma warning restore 4014

"4014"इस MSDN पेज से आता है: संकलक चेतावनी (स्तर 1) CS4014

@ Ryan-horath यहाँ https://stackoverflow.com/a/12145047/928483 द्वारा चेतावनी / उत्तर भी देखें ।

एक async कॉल के दौरान फेंके गए अपवाद जो प्रतीक्षित नहीं हैं वे खो जाएंगे। इस चेतावनी से छुटकारा पाने के लिए, आपको async कॉल के टास्क रिटर्न वैल्यू को एक वैरिएबल पर असाइन करना चाहिए। यह सुनिश्चित करता है कि आपके पास फेंके गए किसी भी अपवाद तक पहुंच है, जो कि वापसी मूल्य में इंगित किया जाएगा।

C # 7.0 के लिए अपडेट करें

C # 7.0 एक नई सुविधा जोड़ता है, चरों को त्यागें: Discards - C # Guide , जो इस संबंध में भी मदद कर सकता है।

_ = SomeMethodAsync();

5
यह बिल्कुल भयानक है। मुझे नहीं पता था कि आप ऐसा कर सकते हैं। धन्यवाद!
मैक्सिम गेर्शकोविच

1
कहने की आवश्यकता भी नहीं है var, बस लिखें_ = SomeMethodAsync();
रे

41

मैं विशेष रूप से उन समाधानों का शौकीन नहीं हूं जो या तो अप्रयुक्त चर को कार्य सौंपते हैं, या शून्य को वापस करने के लिए विधि हस्ताक्षर बदलते हैं। पूर्व शानदार, गैर-सहज ज्ञान युक्त कोड बनाता है, जबकि उत्तरार्द्ध संभव नहीं हो सकता है यदि आप एक इंटरफ़ेस लागू कर रहे हैं या फ़ंक्शन का एक और उपयोग है जहां आप लौटे हुए कार्य का उपयोग करना चाहते हैं।

मेरा समाधान टास्क का एक विस्तार तरीका बनाना है, जिसे DoNotAwait () कहा जाता है जो कुछ भी नहीं करता है। यह न केवल सभी चेतावनियों, ReSharper या अन्यथा को दबाएगा, बल्कि कोड को और अधिक समझने योग्य बना देगा, और आपके कोड के भावी अनुरक्षकों को इंगित करता है कि आपने वास्तव में कॉल का इंतजार नहीं किया है।

विस्तार विधि:

public static class TaskExtensions
{
    public static void DoNotAwait(this Task task) { }
}

उपयोग:

public static void DoStuff()
{
    GetNameAsync().DoNotAwait();
    MainWorkOfApplicationIDontWantBlocked();
}

जोड़ने के लिए संपादित: यह जोनाथन एलन के समाधान के समान है जहां विस्तार विधि कार्य शुरू करेगी यदि पहले से ही शुरू नहीं हुआ है, लेकिन मैं एकल-उद्देश्य वाले कार्य करना पसंद करता हूं ताकि कॉलर का इरादा पूरी तरह से स्पष्ट हो।


1
मुझे यह पसंद है, लेकिन मैंने इसका नाम बदलकर Unawait ();)
Ostati

29

async void बुरा है!

  1. शून्य को लौटाने और टास्क को वापस करने में क्या अंतर है?
  2. https://jaylee.org/archive/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async-void.html

मेरा सुझाव है कि आप Taskएक गुमनाम विधि के माध्यम से स्पष्ट रूप से चलाएं ...

जैसे

public static void DoStuff()
{
    Task.Run(async () => GetNameAsync());
    MainWorkOfApplicationIDontWantBlocked();
}

या यदि आप चाहते थे कि यह ब्लॉक हो जाए तो आप अनाम पद्धति पर इंतजार कर सकते हैं

public static void DoStuff()
{
    Task.Run(async () => await GetNameAsync());
    MainWorkOfApplicationThatWillBeBlocked();
}

हालाँकि, यदि आपके GetNameAsyncविधि में UI या कुछ भी यूआई बाउंड, (WinRT / MVVM, मैं आपको देख रहा हूं) के साथ बातचीत करना है, तो यह थोड़ा मजेदार हो जाता है =)

आपको यूआई डिस्पैचर के संदर्भ को इस तरह से पास करना होगा ...

Task.Run(async () => await GetNameAsync(CoreApplication.MainView.CoreWindow.Dispatcher));

और फिर अपने async विधि में आपको अपने UI या UI बाउंड तत्वों के साथ बातचीत करने की आवश्यकता होगी जो कि डिस्पैचर ...

dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => {  this.UserName = userName; });

आपका टास्क एक्सटेंशन कमाल का है। पता नहीं क्यों मैं एक से पहले लागू नहीं किया है।
मिकेल डूई बोलिंदर

8
आपके द्वारा उल्लिखित पहला दृष्टिकोण एक अलग चेतावनी का कारण बनता है: This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread. यह भी एक नया धागा बनाने का कारण बनता है, जबकि एक नया धागा आवश्यक रूप से केवल async / प्रतीक्षा के साथ नहीं बनाया जाएगा।
gregsdennis

आपको ऊपर होना चाहिए सर!
ओजकुन

16

यह मैं वर्तमान में कर रहा हूँ:

SomeAyncFunction().RunConcurrently();

जहां RunConcurrentlyके रूप में परिभाषित किया गया है ...

 /// <summary> 
 /// Runs the Task in a concurrent thread without waiting for it to complete. This will start the task if it is not already running. 
 /// </summary> 
 /// <param name="task">The task to run.</param> 
 /// <remarks>This is usually used to avoid warning messages about not waiting for the task to complete.</remarks> 
 public static void RunConcurrently(this Task task) 
 { 
     if (task == null) 
         throw new ArgumentNullException("task", "task is null."); 

     if (task.Status == TaskStatus.Created) 
         task.Start(); 
 } 

https://github.com/docevaad/Anchor/blob/master/Tortuga.Anchor/Tortuga.Anchor.source/shared/TaskUtilities.cs

https://www.nuget.org/packages/Tortuga.Anchor/


1
+1। ऐसा लगता है कि यह अन्य उत्तरों की टिप्पणियों की तुलना में सभी समस्याओं से बचा जाता है। धन्यवाद।
ग्रेल्ट

3
आपको बस इसकी आवश्यकता है: public static void Forget(this Task task) { }
शीतल शाह

1
आपका क्या मतलब है @ शीतलशाह
जय विक

@ शीतलशाह केवल ऑटो-स्टार्टिंग कार्यों के साथ काम करेगा जैसे कि उन लोगों के साथ बनाया गया async Task। कुछ कार्यों को मैन्युअल रूप से शुरू करने की आवश्यकता है।
जोनाथन एलन

7

इस चेतावनी पर Microsoft लेख के अनुसार, आप इसे केवल दिए गए कार्य को एक चर पर असाइन करके हल कर सकते हैं। नीचे Microsoft उदाहरण में दिए गए कोड का अनुवाद है:

    // To suppress the warning without awaiting, you can assign the 
    // returned task to a variable. The assignment doesn't change how
    // the program runs. However, the recommended practice is always to
    // await a call to an async method.
    // Replace Call #1 with the following line.
    Task delayTask = CalledMethodAsync(delay);

ध्यान दें कि ऐसा करने से "स्थानीय वैरिएबल का उपयोग कभी नहीं किया जाता है" का परिणाम ReSharper में होगा।


हां, यह चेतावनी को दबा देता है, लेकिन नहीं, यह वास्तव में कुछ भी हल नहीं करता है। जब तक आपको बहुत अच्छा कारण नहीं मिल जाता है, तब तक Taskकार्य करना-होना चाहिए await। यहां कोई कारण नहीं है कि किसी async voidविधि का उपयोग करने के पहले से ही स्वीकृत उत्तर की तुलना में कार्य को छोड़ना बेहतर होगा ।

मैं शून्य विधि भी पसंद करता हूं। यह Microsoft की तुलना में StackOverflow को अधिक स्मार्ट बनाता है, लेकिन निश्चित रूप से इसे दिया जाना चाहिए।
देवशयनी

4
async voidत्रुटि से निपटने के लिए गंभीर समस्याओं का परिचय देता है और परिणामी-परीक्षण योग्य कोड ( मेरा एमएसडीएन लेख देखें )। चर का उपयोग करना कहीं बेहतर होगा - यदि आप पूरी तरह से सुनिश्चित हैं कि आप अपवादों को चुपचाप निगलना चाहते हैं। अधिक संभावना है, सेशन दो Taskएस शुरू करना चाहता है और फिर ए करेगा await Task.WhenAll
स्टीफन क्लीयर

@StephenCleary अप्राप्य कार्यों से अपवादों को नजरअंदाज किया जाता है या नहीं। यदि कोई मौका है तो आपका कोड किसी और के प्रोजेक्ट में उपयोग किया जाएगा, ऐसा न होने दें। आपके द्वारा वर्णित विधियों async void DoNotWait(Task t) { await t; }की कमियों से बचने के लिए एक सरल सहायक विधि का उपयोग किया जा सकता है async void। (और मुझे नहीं लगता Task.WhenAllकि ओपी क्या चाहता है, लेकिन यह बहुत अच्छी तरह से हो सकता है।)

@hvd: आपकी सहायक विधि इसे परीक्षण योग्य बना सकती है, लेकिन इसमें अभी भी बहुत खराब अपवाद हैंडलिंग समर्थन होगा।
स्टीफन क्लीयर

3

यहाँ, एक सरल समाधान।

public static class TasksExtensions
{
    public static void RunAndForget(this Task task)
    {
    }
}

सादर


1

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

यदि आपके पास वास्तव में ऐसा कुछ है जो बाकी प्रोग्राम से पूरी तरह से अलग है, तो async सही दृष्टिकोण नहीं होगा। बस उस कार्य के लिए एक नया सूत्र शुरू करें।


मैं अवरुद्ध स्रोत से प्राप्त डेटा का उपयोग करना चाहता हूं , लेकिन मैं इसके इंतजार के दौरान कॉलर को ब्लॉक नहीं करना चाहता। Async के बिना, आप कॉलबैक में पास करके इसे पूरा कर सकते हैं। मेरे उदाहरण में, मेरे पास दो अतुल्यकालिक विधियां हैं जिन्हें अनुक्रम में बुलाया जाना चाहिए, और दूसरी कॉल के लिए पहले द्वारा लौटाए गए मान का उपयोग करने की आवश्यकता है। इसका अर्थ होगा नेस्टिंगबैक हैंडलर, जो नरक के रूप में बदसूरत हो जाता है। मैंने जो डॉक्यूमेंटेशन पढ़ा है, वह बताता है कि यह विशेष asyncरूप से साफ करने के लिए डिज़ाइन किया गया है ( उदाहरण के लिए )
Mud

@Mud: बस पहले async कॉल से परिणाम को दूसरे कॉल के पैरामीटर के रूप में भेजें। इस तरह दूसरी विधि में कोड तुरंत शुरू होता है, और यह पहली कॉल से परिणाम का इंतजार कर सकता है जब उसे ऐसा लगता है।
गुफ़ा

मैं ऐसा कर सकता हूं, लेकिन बिना async का मतलब है नेस्टेड कॉलबैक, इस तरह: MethodWithCallback((result1) => { Use(result1); MethodWithCallback((result2) => { Use(result1,result2); })यहां तक ​​कि इस तुच्छ उदाहरण में, यह पार्स करने के लिए एक कुतिया है। Async के साथ, मेरे लिए समतुल्य कोड तब उत्पन्न होता है जब मैं लिखता हूं result1 = await AsyncMethod(); Use(result1); result2 = await AsyncMethod(); Use(result1,result2); जो पढ़ने में बहुत आसान है (यद्यपि इस टिप्पणी में न तो बहुत पठनीय एक साथ स्मोक किया जाता है!)
Mud

@ मूड: हाँ। लेकिन आप पहले के बाद दूसरा async विधि कह सकते हैं, इसके लिए कोई कारण नहीं है कि वह सिंक्रोनस कॉल के लिए प्रतीक्षा करे Use
गुफ़ा

मुझे वास्तव में वह नहीं मिला जो आप कह रहे हैं। MethodWithCallback एसिंक्रोनस है। यदि मैं पहली कॉल पर प्रतीक्षा किए बिना उन्हें बैक टू बैक कॉल करता हूं, तो दूसरी कॉल पहली से पहले समाप्त हो सकती है। हालांकि, मुझे दूसरे के लिए हैंडलर में पहले कॉल के परिणाम की आवश्यकता है। इसलिए मुझे पहले कॉल पर इंतजार करना होगा।
मड

0

क्या आप वास्तव में परिणाम को अनदेखा करना चाहते हैं? के रूप में किसी भी अप्रत्याशित अपवाद की अनदेखी में शामिल हैं?

यदि आप इस प्रश्न पर एक नज़र नहीं डालना चाहते हैं: आग और भूल दृष्टिकोण ,


0

यदि आप लौटने के लिए विधि हस्ताक्षर को बदलना नहीं चाहते हैं void(जैसा कि रिटर्निंग voidहमेशा शून्य संस्करण होना चाहिए ), तो आप इस तरह से C # 7.0+ डिस्कार्ड सुविधा का उपयोग कर सकते हैं , जो एक चर को असाइन करने की तुलना में थोड़ा बेहतर है (और अन्य को हटा देना चाहिए) स्रोत सत्यापन उपकरण चेतावनी):

public static void DoStuff()
{
    _ = GetNameAsync(); // we don't need the return value (suppresses warning)
    MainWorkOfApplicationIDontWantBlocked();
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.