C # में "वापसी का इंतजार" का उद्देश्य क्या है?


251

वहाँ है किसी भी परिदृश्य में जहाँ इस तरह विधि लेखन:

public async Task<SomeResult> DoSomethingAsync()
{
    // Some synchronous code might or might not be here... //
    return await DoAnotherThingAsync();
}

इसके अलावा:

public Task<SomeResult> DoSomethingAsync()
{
    // Some synchronous code might or might not be here... //
    return DoAnotherThingAsync();
}

समझ में आएगा?

return awaitजब आप सीधे Task<T>आंतरिक DoAnotherThingAsync()आह्वान से वापस लौट सकते हैं तो निर्माण का उपयोग क्यों करें ?

मुझे return awaitकई स्थानों पर कोड दिखाई दे रहे हैं, मुझे लगता है कि मुझे कुछ याद आ गया होगा। लेकिन जहाँ तक मैं समझता हूँ, इस मामले में async / wait कीवर्ड का उपयोग नहीं करना और सीधे टास्क को वापस करना कार्यात्मक रूप से समतुल्य होगा। अतिरिक्त awaitपरत के अतिरिक्त ओवरहेड को क्यों जोड़ें ?


2
मुझे लगता है कि केवल यही कारण है कि आप इसे देखते हैं क्योंकि लोग नकल द्वारा सीखते हैं और आम तौर पर (यदि उन्हें ज़रूरत नहीं है) वे सबसे सरल समाधान का उपयोग करते हैं जो वे पा सकते हैं। तो लोग उस कोड को देखते हैं, उस कोड का उपयोग करते हैं, वे देखते हैं कि यह काम करता है और अब से, उनके लिए, यह करने का सही तरीका है ... यह उस मामले में इंतजार करने का कोई फायदा नहीं है
Fabio Marcolini

7
कम से कम एक महत्वपूर्ण अंतर है: अपवाद प्रचार
noseratio

1
मुझे यह समझ में नहीं आता, खिचड़ी भाषा इस पूरी अवधारणा को समझती है, कोई मतलब नहीं है। यदि किसी विधि में रिटर्न टाइप है, तो मैंने जो सीखा है, उसमें से एक रिटर्न कीवर्ड होना चाहिए, क्या यह C # भाषा के नियम नहीं हैं?
15

@ ओम्स्ट्रो ओपी के प्रश्न में हालांकि रिटर्न स्टेटमेंट है?
डेविड क्लेम्फनर

जवाबों:


189

वहाँ जब एक डरपोक मामला है returnसामान्य विधि में और return awaitमें asyncविधि व्यवहार को अलग ढंग से: जब साथ संयुक्त using(या अधिक सामान्य रूप से किसी भी return awaitएक में tryब्लॉक)।

एक विधि के इन दो संस्करणों पर विचार करें:

Task<SomeResult> DoSomethingAsync()
{
    using (var foo = new Foo())
    {
        return foo.DoAnotherThingAsync();
    }
}

async Task<SomeResult> DoSomethingAsync()
{
    using (var foo = new Foo())
    {
        return await foo.DoAnotherThingAsync();
    }
}

पहली विधि होगा वस्तु जैसे ही विधि रिटर्न, इससे पहले कि यह वास्तव में पूरा करता है जो संभावना लंबा है। इसका मतलब यह है कि पहला संस्करण संभवतः छोटी गाड़ी है (क्योंकि बहुत जल्द निपटारा हो जाता है), जबकि दूसरा संस्करण ठीक काम करेगा।Dispose()FooDoAnotherThingAsync()Foo


4
पूर्णता के लिए, पहले मामले में आपको वापस आना चाहिएfoo.DoAnotherThingAsync().ContinueWith(_ => foo.Dispose());
ghord

7
@ वह काम नहीं करेगा, Dispose()रिटर्न void। आपको कुछ इस तरह की आवश्यकता होगी return foo.DoAnotherThingAsync().ContinueWith(t -> { foo.Dispose(); return t.Result; });। लेकिन मुझे नहीं पता कि आप ऐसा क्यों करेंगे जब आप दूसरे विकल्प का उपयोग कर सकते हैं।
svick

1
@svick आप सही हैं, यह रेखाओं के साथ अधिक होना चाहिए { var task = DoAnotherThingAsync(); task.ContinueWith(_ => foo.Dispose()); return task; }। उपयोग का मामला बहुत सरल है: यदि आप .NET 4.0 (अधिकांश की तरह) पर हैं, तो आप अभी भी async कोड इस तरह से लिख सकते हैं, जो 4.5 ऐप्स से अच्छी तरह से कॉल किया जाएगा।
गॉर्ड

2
@ghord यदि आप .Net 4.0 पर हैं और आप एसिंक्रोनस कोड लिखना चाहते हैं, तो आपको संभवतः Microsoft.Bcl.Async का उपयोग करना चाहिए । और आपका कोड Fooकेवल रिटर्न Taskपूर्ण होने के बाद ही निपटता है , जो मुझे पसंद नहीं है, क्योंकि यह अनावश्यक रूप से संगामिति का परिचय देता है।
svick

1
@svick आपका कोड तब तक प्रतीक्षा करता है जब तक कि कार्य समाप्त नहीं हो जाता। इसके अलावा, Microsoft.Bcl.Async KB2468871 पर निर्भरता के कारण मेरे लिए अनुपयोगी है और उचित 4.5 async कोड वाले .NET 4.0 async कोडबेस का उपयोग करते समय टकराव होता है।
गॉर्ड

93

अगर आपको जरूरत नहीं है async(यानी, आप Taskसीधे लौट सकते हैं ), तो उपयोग न करें async

कुछ स्थितियां ऐसी हैं return awaitजो उपयोगी हैं, जैसे कि यदि आपके पास करने के लिए दो अतुल्यकालिक ऑपरेशन हैं:

var intermediate = await FirstAsync();
return await SecondAwait(intermediate);

asyncप्रदर्शन पर अधिक जानकारी के लिए , स्टीफन टूब के MSDN लेख और विषय पर वीडियो देखें

अपडेट: मैंने एक ब्लॉग पोस्ट लिखी है जो बहुत अधिक विवरण में है।


13
क्या आप एक स्पष्टीकरण जोड़ सकते हैं awaitकि दूसरे मामले में क्यों उपयोगी है? क्यों नहीं करते return SecondAwait(intermediate);?
मैट स्मिथ

2
मैं मैट के रूप में एक ही सवाल है, return SecondAwait(intermediate);उस मामले में भी लक्ष्य हासिल नहीं करेगा ? मुझे लगता return awaitहै कि यहाँ भी बेमानी है ...
TX_

23
@MattSmith संकलन नहीं करेगा। यदि आप awaitपहली पंक्ति में उपयोग करना चाहते हैं , तो आपको इसे दूसरी पंक्ति में भी उपयोग करना होगा।
svick

2
@ cateyes मुझे यकीन नहीं है कि "ओवरहेड उन्हें पेश करने से क्या मतलब है" का अर्थ है, लेकिन asyncसंस्करण आपके तुल्यकालिक संस्करण की तुलना में कम संसाधनों (थ्रेड्स) का उपयोग करेगा ।
svick

4
@TomLint यह वास्तव में संकलन नहीं है। वापसी प्रकार मानते हुए SecondAwait'स्ट्रिंग' है, त्रुटि संदेश है: "CS4016: चूंकि यह एक async विधि है, इसलिए रिटर्न एक्सप्रेशन 'टास्क <string>' के बजाय टाइप 'स्ट्रिंग' का होना चाहिए।"
svick

23

यदि आप ऐसा करना चाहते हैं तो एकमात्र कारण यह है awaitकि पहले के कोड में कुछ और है, या यदि आप किसी तरह से इसे वापस करने से पहले परिणाम में हेरफेर कर रहे हैं। एक और तरीका जिसमें यह हो सकता है try/catchकि परिवर्तन के माध्यम से कैसे अपवादों को नियंत्रित किया जाता है। यदि आप उस में से कोई भी नहीं कर रहे हैं तो आप सही हैं, विधि बनाने के ओवरहेड को जोड़ने का कोई कारण नहीं है async


4
स्टीफन के जवाब के साथ, मुझे समझ में नहीं आता कि पहले कोड में कुछ अन्य प्रतीक्षा होने के बावजूद (return await बच्चे के आह्वान के कार्य को वापस करने के बजाय) आवश्यक क्यों होगा । क्या आप कृपया स्पष्टीकरण प्रदान कर सकते हैं?
TX_

10
@TX_ यदि आप हटाना चाहते हैं asyncतो आप पहले कार्य की प्रतीक्षा कैसे करेंगे? आपको विधि को चिह्नित करने की आवश्यकता है जैसे asyncकि आप किसी भी प्रतीक्षा का उपयोग करना चाहते हैं । यदि विधि के रूप में चिह्नित किया गया है asyncऔर आपके पास awaitपहले वाला कोड है, तो आपको awaitउचित प्रकार का होने के लिए दूसरे async ऑपरेशन की आवश्यकता है । यदि आपने अभी हटा दिया है awaitतो यह संकलित नहीं करेगा क्योंकि वापसी मूल्य उचित प्रकार का नहीं होगा। चूंकि विधि एक asyncपरिणाम है जो हमेशा एक कार्य में लिपटा रहता है।
सेवी

11
@ नोसरियोटी दो को आज़माएं। पहला संकलन। दूसरा नहीं है। त्रुटि संदेश आपको समस्या बताएगा। आप उचित प्रकार नहीं लौटाएंगे। जब एक asyncविधि में आप किसी कार्य को वापस नहीं करते हैं, तो आप उस कार्य के परिणाम को वापस करते हैं जो तब लपेटा जाएगा।
सेवि

3
@ सेरी, बिल्कुल - आप सही कह रहे हैं। बाद के मामले में हम Task<Type>स्पष्ट रूप से लौट आएंगे , जबकि asyncवापस लौटने के लिए निर्देश Type(जो संकलक खुद में बदल जाएगा Task<Type>)।
noseratio

3
@ निश्चित रूप से, निश्चित asyncरूप से निरंतरता के लिए वायरिंग शुगर है। आप नहीं है की जरूरत है async कुछ भी करने को है, लेकिन जब किसी भी गैर तुच्छ अतुल्यकालिक ऑपरेशन के बारे में बस कर यह नाटकीय रूप से साथ काम करने के लिए आसान। उदाहरण के लिए, आपके द्वारा प्रदान किया गया कोड वास्तव में त्रुटियों का प्रचार नहीं करता है जैसा कि आप इसे चाहते हैं, और इससे भी अधिक जटिल परिस्थितियों में ठीक से करना काफी कठिन हो जाता है। जब आपको कभी भी आवश्यकता नहीं होती हैasync , तो जिन स्थितियों का मैं वर्णन करता हूं, वे इसका उपयोग करने के लिए मूल्य जोड़ रहे हैं।
सेवक

17

एक और मामला आपको परिणाम का इंतजार करना पड़ सकता है यह एक है:

async Task<IFoo> GetIFooAsync()
{
    return await GetFooAsync();
}

async Task<Foo> GetFooAsync()
{
    var foo = await CreateFooAsync();
    await foo.InitializeAsync();
    return foo;
}

इस मामले में, GetIFooAsync()परिणाम का इंतजार करना चाहिए GetFooAsyncक्योंकि Tदोनों विधियों के बीच का प्रकार अलग है और Task<Foo>सीधे करने के लिए उपलब्ध नहीं है Task<IFoo>। लेकिन अगर आप रिजल्ट का इंतजार करते हैं, तो यह ठीक वैसा ही हो जाता है, Fooजो सीधे-सीधे लागू होता हैIFoo । तब async विधि Task<IFoo>आपके द्वारा जाने के अंदर और बाहर परिणाम को दोहराती है ।


1
सहमत हूँ, यह वास्तव में कष्टप्रद है - मेरा मानना ​​है कि अंतर्निहित कारण यह है कि Task<>अपरिवर्तनीय है।
स्टुअर्टएलसी

7

अन्यथा सरल "थंक" विधि async बनाना स्मृति में एक async राज्य मशीन बनाता है जबकि गैर- async कोई नहीं करता है। हालांकि यह अक्सर लोगों को गैर-एस्किंक संस्करण का उपयोग करने पर इंगित कर सकता है क्योंकि यह अधिक कुशल है (जो सच है) इसका मतलब यह भी है कि हैंग होने की स्थिति में, आपके पास कोई सबूत नहीं है कि यह विधि "वापसी / निरंतरता स्टैक" में शामिल है। जो कभी-कभी हैंग को समझना और भी मुश्किल बना देता है।

हां, जब perf महत्वपूर्ण नहीं है (और आमतौर पर ऐसा नहीं होता है) तो मैं इन सभी थंक विधियों पर async फेंक दूंगा ताकि बाद में मुझे हैंग होने का पता लगाने में मदद करने के लिए async स्टेट मशीन हो, और यह सुनिश्चित करने में भी मदद मिल सके कि यदि वे थंक तरीके कभी समय के साथ विकसित होते हैं, वे फेंकने के बजाय दोषपूर्ण कार्यों को वापस करना सुनिश्चित करेंगे।


6

यदि आप डिबगिंग के दौरान अपने स्टैक ट्रेस को बर्बाद कर सकते हैं या जब यह अपवादों पर लॉग में मुद्रित होता है, तो आप वेट रिटर्न का उपयोग नहीं करेंगे।

जब आप कार्य वापस करते हैं, तो विधि ने अपना उद्देश्य पूरा किया और यह कॉल स्टैक से बाहर है। जब आप उपयोग return awaitकरते हैं तो आप इसे कॉल स्टैक में छोड़ रहे हैं।

उदाहरण के लिए:

प्रतीक्षा करते समय कॉल स्टैक: A को B से कार्य की प्रतीक्षा करना> C से कार्य की प्रतीक्षा करना

प्रतीक्षा करने पर स्टैक कॉल करें : C से कार्य का इंतजार है, जो B ने वापस कर दिया है।


2

इससे मुझे भी भ्रम होता है और मुझे लगता है कि पिछले उत्तरों ने आपके वास्तविक प्रश्न को अनदेखा कर दिया था:

जब आप सीधे DoAnotherThingAsync () इनवोकेशन से टास्क को वापस कर सकते हैं, तो वेट रिटर्न निर्माण का उपयोग क्यों करें?

खैर कभी-कभी आप वास्तव में एक चाहते हैं Task<SomeType>, लेकिन अधिकांश समय आप वास्तव में एक उदाहरण चाहते हैं, अर्थात् SomeType, कार्य से परिणाम।

आपके कोड से:

async Task<SomeResult> DoSomethingAsync()
{
    using (var foo = new Foo())
    {
        return await foo.DoAnotherThingAsync();
    }
}

वाक्यविन्यास (मुझे, उदाहरण के लिए) से अपरिचित व्यक्ति सोच सकता है कि इस विधि को वापस आ जाना चाहिए Task<SomeResult>, लेकिन चूंकि इसे चिह्नित किया गया है async, इसका मतलब है कि इसका वास्तविक रिटर्न प्रकार है SomeResult। यदि आप अभी उपयोग करते हैं return foo.DoAnotherThingAsync(), तो आप एक टास्क लौटा देंगे, जो संकलन नहीं करेगा। सही तरीका कार्य के परिणाम को वापस करना है, इसलिए return await


1
"वास्तविक वापसी प्रकार"। एह? async / प्रतीक्षा वापसी प्रकार नहीं बदल रहा है। आपके उदाहरण में var task = DoSomethingAsync();आपको एक कार्य दिया जाएगा, न किT
जूता

2
@ शो मुझे यकीन नहीं है कि मैं अच्छी तरह से async/awaitबात समझ गया । मेरी समझ में Task task = DoSomethingAsync(), जबकि Something something = await DoSomethingAsync()दोनों काम करते हैं। पहला आपको कार्य उचित देता है, जबकि दूसरा, awaitकीवर्ड के कारण , कार्य पूरा होने के बाद आपको परिणाम देता है । मैं कर सकता हूँ, उदाहरण के लिए, है Task task = DoSomethingAsync(); Something something = await task;
हेल्टनबिकर 18
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.