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


156

यह "बिना किसी प्रतीक्षा के C # में एक Async विधि को सुरक्षित रूप से कॉल करने का तरीका" का डुप्लिकेट नहीं है ।

मैं निम्नलिखित चेतावनी को अच्छी तरह से कैसे दबाऊं?

चेतावनी CS4014: क्योंकि यह कॉल प्रतीक्षित नहीं है, कॉल पूरा होने से पहले वर्तमान पद्धति का निष्पादन जारी है। कॉल के परिणाम के लिए 'प्रतीक्षा' ऑपरेटर को लागू करने पर विचार करें।

एक सरल उदाहरण:

static async Task WorkAsync()
{
    await Task.Delay(1000);
    Console.WriteLine("Done!");
}

static async Task StartWorkAsync()
{
    WorkAsync(); // I want fire-and-forget 

    // more unrelated async/await stuff here, e.g.:
    // ...
    await Task.Delay(2000); 
}

मैंने क्या कोशिश की और क्या पसंद नहीं आया:

static async Task StartWorkAsync()
{
    #pragma warning disable 4014
    WorkAsync(); // I want fire-and-forget here
    #pragma warning restore 4014
    // ...
}

static async Task StartWorkAsync()
{
    var ignoreMe = WorkAsync(); // I want fire-and-forget here
    // ...
}

अपडेट किया गया , चूंकि मूल स्वीकार किए गए उत्तर को संपादित किया गया है, इसलिए मैंने C # 7.0 डिस्क्स का उपयोग करके स्वीकार किए गए उत्तर को बदल दिया है , क्योंकि मुझे नहीं लगता कि ContinueWithयह उपयुक्त है। जब भी मुझे आग-और-भूल संचालन के लिए अपवादों को लॉग करने की आवश्यकता होती है, मैं स्टीफन क्लीरी द्वारा प्रस्तावित अधिक विस्तृत दृष्टिकोण का उपयोग करता हूं ।


1
तो, आपको लगता #pragmaहै कि अच्छा नहीं है?
फ्रैडरिक हमीदी

10
@ Frédéric Hamidi, मैं करता हूं।
noseratio

2
@ नोसरियो: आह, ठीक है। क्षमा करें, मुझे लगा कि यह दूसरी चेतावनी थी। मुझपर ध्यान मत दो!
जॉन स्कीट

3
@Terribad: मुझे वास्तव में यकीन नहीं है - ऐसा लगता है कि ज्यादातर मामलों के लिए चेतावनी बहुत उचित है। विशेष रूप से, आपको यह सोचना चाहिए कि आप किसी भी असफलता के लिए क्या करना चाहते हैं - आमतौर पर "आग और भूल" के लिए भी आपको काम करना चाहिए कि विफलताओं को कैसे लॉग किया जाए आदि
जॉन स्कीट

4
@Terribad, इससे पहले कि आप इसे इस तरह या किसी अन्य का उपयोग करें, आपके पास एक स्पष्ट तस्वीर होनी चाहिए कि कैसे async विधियों के लिए प्रचारित किया जाता है ( इसे देखें )। फिर, @ कन्निस द्वारा जवाब अग्नि- विस्मृति के लिए किसी भी अपवाद को छोड़ने का एक सुंदर तरीका प्रदान करता है , एक async voidसहायक विधि के माध्यम से ।
noseratio

जवाबों:


160

C # 7 के साथ अब आप डिस्क का उपयोग कर सकते हैं :

_ = WorkAsync();

7
यह एक आसान सा भाषा फीचर है जिसे मैं अभी याद नहीं कर सकता। यह ऐसा है जैसे _ = ...मेरे मस्तिष्क में है।
मार्क एल।

3
मैंने पाया कि एक सुपरमेसजेज ने मेरे विजुअल स्टूडियो "एरर लिस्ट" से मेरी चेतावनी को हटा दिया, लेकिन "आउटपुट" को नहीं और #pragma warning disable CSxxxxत्यागने की तुलना में अधिक बदसूरत लग रहा है;)
डेविड सेवेज

122

आप एक विस्तार विधि बना सकते हैं जो चेतावनी को रोक देगा। एक्सटेंशन विधि खाली हो सकती है या आप अपवाद हैंडलिंग को .ContinueWith()वहां जोड़ सकते हैं ।

static class TaskExtensions
{
    public static void Forget(this Task task)
    {
        task.ContinueWith(
            t => { WriteLog(t.Exception); },
            TaskContinuationOptions.OnlyOnFaulted);
    }
}

public async Task StartWorkAsync()
{
    this.WorkAsync().Forget();
}

हालाँकि ASP.NET रनिंग कार्यों की संख्या को गिनाता है, इसलिए यह Forget()ऊपर दिए गए सरल एक्सटेंशन के साथ काम नहीं करेगा और इसके बजाय अपवाद के साथ विफल हो सकता है:

एक एसिंक्रोनस मॉड्यूल या हैंडलर पूरा हुआ जबकि एक एसिंक्रोनस ऑपरेशन अभी भी लंबित था।

.NET 4.5.2 के साथ इसका उपयोग करके हल किया जा सकता है HostingEnvironment.QueueBackgroundWorkItem:

public static Task HandleFault(this Task task, CancellationToken cancelToken)
{
    return task.ContinueWith(
        t => { WriteLog(t.Exception); },
        cancelToken,
        TaskContinuationOptions.OnlyOnFaulted,
        TaskScheduler.Default);
}

public async Task StartWorkAsync()
{
    System.Web.Hosting.HostingEnvironment.QueueBackgroundWorkItem(
        cancelToken => this.WorkAsync().HandleFault(cancelToken));
}

8
मैंने पाया है TplExtensions.Forget। के तहत बहुत अधिक अच्छाई है Microsoft.VisualStudio.Threading। काश, इसे विजुअल स्टूडियो एसडीके के बाहर उपयोग के लिए उपलब्ध कराया जाता।
noseratio

1
@ नोसेरियोटी, और नेगीस, मुझे यह दृष्टिकोण पसंद है और मैं इसका उपयोग करने की योजना बना रहा हूं। मैंने एक संबंधित अनुवर्ती प्रश्न पोस्ट किया: stackoverflow.com/questions/22864367/fire-and-forget-approach
मैट स्मिथ

3
@stricq क्या कॉन्फिगरएविट (झूठा) को भूल जाने () सेवा करने के लिए जोड़ा जाएगा? जैसा कि मैं इसे समझता हूं, कन्फिगरवाइट केवल उस बिंदु पर थ्रेड सिंक को प्रभावित करता है जहां टास्क का उपयोग किया जाता है, लेकिन फोर्ज़ () का उद्देश्य टास्क को दूर करना है, इसलिए टास्क का कभी भी इंतजार नहीं किया जा सकता है, इसलिए यहां कॉन्फ़िगर करेंवाइट व्यर्थ है।
dthorpe

3
यदि स्पॉनिंग थ्रेड आग से पहले दूर हो जाता है और कार्य पूरा नहीं होता है, तो कॉन्फिगरएविट (गलत) के बिना, यह अभी भी स्पार्स थ्रेड पर वापस मार्शॉल करने की कोशिश करेगा, यह थ्रेड डेडलॉक हो गया है। ConfigureAwait (गलत) सेट करना कॉलिंग थ्रेड पर सिस्टम को वापस मार्श न करने के लिए कहता है।
तारिख

2
इस उत्तर में एक विशिष्ट मामले का प्रबंधन करने के लिए एक संपादन है, साथ ही टिप्पणियों के एक दर्जन। बस चीजें अक्सर सही चीजें होती हैं, डिस्क के लिए जाएं! और मैं @ fjch1997 उत्तर उद्धृत करता हूं: यह एक ऐसी विधि बनाने के लिए बेवकूफी है जो किसी चेतावनी को दबाने के उद्देश्य से निष्पादित करने के लिए कुछ और टिक लेता है।
तीजय

39

आप निम्न विशेषता से विधि को सजा सकते हैं:

[System.Diagnostics.CodeAnalysis.SuppressMessage("Await.Warning", "CS4014:Await.Warning")]
static async Task StartWorkAsync()
{
    WorkAsync();
    // ...
}

मूल रूप से आप संकलक को बता रहे हैं कि आप जानते हैं कि आप क्या कर रहे हैं और इसे संभावित गलती के बारे में चिंता करने की आवश्यकता नहीं है।

इस कोड का महत्वपूर्ण हिस्सा दूसरा पैरामीटर है। "CS4014:" भाग वह है जो चेतावनी को दबा देता है। बाकी पर आप कुछ भी लिख सकते हैं।


मेरे लिए काम नहीं करता है: मैक 7.0.1 के लिए दृश्य स्टूडियो (24 का निर्माण)। लगता है जैसे यह चाहिए लेकिन - नहीं।
लौहोद

1
[SuppressMessage("Compiler", "CS4014")]त्रुटि सूची विंडो में संदेश को दबाता है, लेकिन आउटपुट विंडो अभी भी एक चेतावनी लाइन दिखाता है
डेविड चिंग

35

इससे निपटने का मेरा दो तरीका है।

इसे एक डिसाइड वेरिएबल में सेव करें (C # 7)

उदाहरण

_ = Task.Run(() => DoMyStuff()).ConfigureAwait(false);

C # 7 में डिस्क की शुरुआत के बाद से, मैं अब इसे चेतावनी को दबाने से बेहतर मानता हूं। क्योंकि यह न केवल चेतावनी को दबा देता है, बल्कि आग और भूल जाने के इरादे को भी स्पष्ट करता है।

इसके अलावा, संकलक इसे रिलीज मोड में दूर अनुकूलित करने में सक्षम होगा।

बस इसे दबा दो

#pragma warning disable 4014
...
#pragma warning restore 4014

"आग और भूल" के लिए एक अच्छा पर्याप्त समाधान है।

यह चेतावनी क्यों मौजूद है इसका कारण यह है कि कई मामलों में यह एक ऐसी पद्धति का उपयोग करने का आपका उद्देश्य नहीं है जो बिना प्रतीक्षा किए कार्य लौटाता है। जब आप आग लगाना और भूल जाना चाहते हैं तब चेतावनी को दबा देना समझ में आता है।

यदि आपको यह याद रखने में परेशानी होती है कि कैसे वर्तनी है #pragma warning disable 4014, तो बस विजुअल स्टूडियो को इसे आपके लिए जोड़ने दें। Ctrl + दबाएं। "त्वरित कार्रवाई" खोलने के लिए और फिर "CS2014 को दबाएं"

सब मिलाकर

यह एक ऐसी विधि बनाने के लिए बेवकूफी है जो एक चेतावनी को दबाने के उद्देश्य से निष्पादित करने के लिए कुछ और टिक लेता है।


इसने मैक 7.0.1 (बिल्ड 24) के लिए विजुअल स्टूडियो में काम किया।
आयरनरोड

1
यह एक ऐसी विधि बनाने के लिए बेवकूफी है जिसे निष्पादित करने के लिए कुछ और टिक करने की ज़रूरत है , बस एक चेतावनी को दबाने के उद्देश्य से - यह एक अतिरिक्त टिक नहीं जोड़ता है और IMO अधिक पठनीय है:[MethodImpl(MethodImplOptions.AggressiveInlining)] void Forget(this Task @this) { } /* ... */ obj.WorkAsync().Forget();
noseratio

1
@Noseratio कई बार जब मैं AggressiveInliningकंपाइलर का उपयोग करता हूँ तो इसे किसी भी कारण से अनदेखा कर देता हूँ
fjch1997

1
मुझे व्यावहारिक विकल्प पसंद है, क्योंकि यह सुपर सरल है और केवल कोड की वर्तमान लाइन (या अनुभाग) पर लागू होता है, पूरी विधि नहीं।
नोचवेयर

2
#pragma warning disable 4014बाद में चेतावनी को पुनर्स्थापित करने के लिए जैसे त्रुटि कोड का उपयोग करना न भूलें #pragma warning restore 4014। यह अभी भी त्रुटि कोड के बिना काम करता है, लेकिन यदि आप त्रुटि संख्या नहीं जोड़ते हैं तो यह सभी संदेशों को दबा देगा।
DunningKrugerEffect

11

चेतावनी को रोकने का एक आसान तरीका बस कॉल करते समय टास्क को असाइन करना है:

Task fireAndForget = WorkAsync(); // No warning now

और इसलिए अपने मूल पद में आप क्या करेंगे:

static async Task StartWorkAsync()
{
    // Fire and forget
    var fireAndForget = WorkAsync(); // Tell the compiler you know it's a task that's being returned 

    // more unrelated async/await stuff here, e.g.:
    // ...
    await Task.Delay(2000); 
}

मैंने इस प्रश्न में ही इस दृष्टिकोण का उल्लेख किया, क्योंकि मैं उनमें से एक को विशेष रूप से पसंद नहीं करता था।
noseratio

ओह! ध्यान नहीं दिया क्योंकि यह एक ही कोड सेक्शन में था जैसा कि आपका प्रागम एक ... और मैं उत्तर ढूंढ रहा था। इसके अलावा, इस विधि के बारे में आपको क्या पसंद नहीं है?
noelicus

1
मुझे यह पसंद नहीं है कि taskएक भूल गए स्थानीय चर की तरह लग रहा है। लगभग संकलक की तरह मुझे एक और चेतावनी देनी चाहिए, जैसे कुछ " taskसौंपा गया है, लेकिन इसका मूल्य कभी उपयोग नहीं किया जाता है", इसके अलावा यह नहीं करता है। साथ ही, यह कोड को कम पठनीय बनाता है। मैं खुद इस दृष्टिकोण का उपयोग करता हूं ।
noseratio

उचित रूप से - मुझे एक समान लग रहा था कि मैं इसे क्यों नाम देता fireAndForgetहूं ... इसलिए मुझे उम्मीद है कि इसके बिना संदर्भ नहीं होगा।
noelicus

4

चेतावनी का कारण WorkAsync है Taskजो कभी पढ़ा या प्रतीक्षित नहीं है। आप करने के लिए WorkAsync का रिटर्न प्रकार सेट कर सकते हैं voidऔर चेतावनी चली जाएगी।

आमतौर पर एक विधि वापस आती है Taskजब कॉलर को कार्यकर्ता की स्थिति जानने की आवश्यकता होती है। आग और भूलने के मामले में, शून्य को फिर से इकट्ठा करने के लिए लौटाया जाना चाहिए कि फोन करने वाले को स्वतंत्र विधि कहा जाता है।

static async void WorkAsync()
{
    await Task.Delay(1000);
    Console.WriteLine("Done!");
}

static async Task StartWorkAsync()
{
    WorkAsync(); // no warning since return type is void

    // more unrelated async/await stuff here, e.g.:
    // ...
    await Task.Delay(2000); 
}

2

क्यों नहीं इसे एक async विधि के अंदर लपेटें जो शून्य देता है? थोड़ा लंबा लेकिन सभी चर का उपयोग किया जाता है।

static async Task StartWorkAsync()
{   
     async void WorkAndForgetAsync() => await WorkAsync();
     WorkAndForgetAsync(); // no warning
}

1

मुझे आज दुर्घटना से यह दृष्टिकोण मिला। आप एक प्रतिनिधि को परिभाषित कर सकते हैं और पहले प्रतिनिधि को async विधि असाइन कर सकते हैं।

    delegate Task IntermediateHandler();



    static async Task AsyncOperation()
    {
        await Task.Yield();
    }

और इसे कॉल करें

(new IntermediateHandler(AsyncOperation))();

...

मुझे लगा कि यह दिलचस्प था कि कंपाइलर प्रतिनिधि का उपयोग करते समय ठीक वैसी ही चेतावनी नहीं देगा।


एक प्रतिनिधि घोषित करने की आवश्यकता नहीं है, आप भी ऐसा कर सकते हैं (new Func<Task>(AsyncOperation))() IMO लेकिन यह अभी भी थोड़ा बहुत क्रिया है।
noseratio
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.