async / प्रतीक्षारत - टास्क बनाम शून्य कब वापस करना है?


502

किस परिदृश्य के तहत कोई भी उपयोग करना चाहेगा

public async Task AsyncMethod(int num)

के बजाय

public async void AsyncMethod(int num)

एकमात्र ऐसा परिदृश्य जिसके बारे में मैं सोच सकता हूं कि क्या आपको इसकी प्रगति को ट्रैक करने में सक्षम होने के लिए कार्य की आवश्यकता है।

इसके अतिरिक्त, निम्नलिखित विधि में, क्या एसिंक्रोनस और प्रतीक्षित कीवर्ड अनावश्यक हैं?

public static async void AsyncMethod2(int num)
{
    await Task.Factory.StartNew(() => Thread.Sleep(num));
}

20
ध्यान दें कि async तरीकों चाहिए हमेशा नाम Async.Example साथ प्रत्यय जा Foo()बन जाएगा FooAsync()
फ्रेड

30
@ फ्रेड ज्यादातर, लेकिन हमेशा नहीं। यह सिर्फ कन्वेंशन है और इस कन्वेंशन के लिए स्वीकृत अपवाद इवेंट आधारित क्लासेस या इंटरफ़ेस कॉन्ट्रैक्ट्स के साथ हैं, MSDN देखें । उदाहरण के लिए, आपको सामान्य ईवेंट हैंडलर्स का नाम नहीं बदलना चाहिए, जैसे कि Button1_Click।
बेन

14
बस एक नोट आप प्रयोग नहीं करना चाहिए Thread.Sleepअपने कार्यों के साथ आपको चाहिए await Task.Delay(num)बजाय
बॉब घाटी

45
@ यदि मैं इससे सहमत नहीं हूं, तो IMO को एक एसिंक्स प्रत्यय को जोड़ना केवल तब उपयोग किया जाना चाहिए जब आप सिंक और एसिंक्स दोनों विकल्पों के साथ एक इंटरफ़ेस प्रदान कर रहे हैं। केवल एक इरादे के होने पर एसिंक्स के साथ चीजों का नामकरण करना व्यर्थ है। बिंदु में मामला Task.Delayनहीं है Task.AsyncDelayAsync कर रहे हैं सभी के रूप में काम पर तरीकों
नहीं प्यार करता था

11
मेरे पास आज सुबह एक वेबपी 2 नियंत्रक विधि के साथ एक दिलचस्प मुद्दा था, इसे async voidइसके बजाय घोषित किया गया था async Task। विधि दुर्घटनाग्रस्त हो गई क्योंकि यह निष्पादित करने के लिए समाप्त होने से पहले नियंत्रक के सदस्य के रूप में घोषित एंटिटी फ्रेमवर्क संदर्भ वस्तु का उपयोग कर रहा था। रूपरेखा ने निष्पादित करने के लिए अपनी विधि समाप्त होने से पहले नियंत्रक को निपटाया। मैंने टास्क को async में बदल दिया और इसने काम किया।
कोस्टा

जवाबों:


417

1) आम तौर पर, आप एक लौटना चाहेंगे Task। मुख्य अपवाद तब होना चाहिए जब आपको आवश्यकता हो एक के लिए void(घटनाओं के लिए) वापसी प्रकार। यदि awaitआपके कार्य को करने वाले को अस्वीकार करने का कोई कारण नहीं है , तो इसे क्यों अस्वीकार करें?

2) asyncविधियाँ जो voidदूसरे पहलू में विशेष हैं: वे शीर्ष-स्तर के async संचालनों का प्रतिनिधित्व करते हैं , और अतिरिक्त नियम हैं जो आपके कार्य को अपवाद के रूप में निभाते हैं। अंतर दिखाने के लिए सबसे आसान तरीका एक उदाहरण के साथ है:

static async void f()
{
    await h();
}

static async Task g()
{
    await h();
}

static async Task h()
{
    throw new NotImplementedException();
}

private void button1_Click(object sender, EventArgs e)
{
    f();
}

private void button2_Click(object sender, EventArgs e)
{
    g();
}

private void button3_Click(object sender, EventArgs e)
{
    GC.Collect();
}

fअपवाद हमेशा "मनाया जाता है"। एक अपवाद जो एक शीर्ष-स्तरीय अतुल्यकालिक विधि को छोड़ देता है, बस उसे किसी अन्य अनहेल्दी अपवाद की तरह माना जाता है। gअपवाद कभी नहीं देखा गया है। जब कचरा संग्रहकर्ता कार्य को साफ करने के लिए आता है, तो यह देखता है कि कार्य एक अपवाद के परिणामस्वरूप हुआ, और किसी ने अपवाद को नहीं संभाला। जब ऐसा होता है,TaskScheduler.UnobservedTaskException हैंडलर चलता है। आपको ऐसा कभी नहीं होने देना चाहिए। अपने उदाहरण का उपयोग करने के लिए,

public static async void AsyncMethod2(int num)
{
    await Task.Factory.StartNew(() => Thread.Sleep(num));
}

हाँ, उपयोग करें async और awaitयहां, वे सुनिश्चित करें कि यदि अपवाद फेंक दिया जाता है, तो आपकी विधि अभी भी सही ढंग से काम करती है।

अधिक जानकारी के लिए देखें: http://msdn.microsoft.com/en-us/magazine/jj991977.aspx


10
मुझे अपनी टिप्पणी के fबजाय मतलब था g। से अपवाद fको पारित किया गया है SynchronizationContextgउठाएंगे UnobservedTaskException, लेकिन UTEअगर यह संभाला नहीं है तो प्रक्रिया को क्रैश नहीं करेगा । कुछ परिस्थितियाँ ऐसी हैं जहाँ "अतुल्यकालिक अपवाद" इस तरह से स्वीकार्य हैं जिन्हें अनदेखा किया जाता है।
स्टीफन क्लीयर

3
यदि आप अपवादों में जिसके परिणामस्वरूप WhenAnyकई Taskएस के साथ है। आपको अक्सर केवल पहले वाले को संभालना होता है, और आप अक्सर दूसरों की अनदेखी करना चाहते हैं।
स्टीफन क्लीयर

1
@StephenCleary धन्यवाद, मुझे लगता है कि यह एक अच्छा उदाहरण है, हालांकि यह इस बात पर निर्भर करता है कि आप WhenAnyपहली जगह में कॉल करते हैं या नहीं, क्या यह अन्य अपवादों को नजरअंदाज करना ठीक है: मेरे पास इसके लिए मुख्य उपयोग का मामला अभी भी शेष कार्यों का इंतजार कर रहा है जब कोई भी जवाब मिलता है एक अपवाद के साथ या बिना।

10
मैं थोड़ा उलझन में हूं कि आप शून्य के बजाय टास्क लौटाने की सलाह क्यों देते हैं। जैसा आपने कहा, च () फेंक देगा और अपवाद होगा लेकिन जी () नहीं होगा। क्या इन पृष्ठभूमि थ्रेड अपवादों से अवगत कराया जाना सबसे अच्छा है?
user981225

2
@ user981225 वास्तव में, लेकिन इसके बाद जी के कॉलर की जिम्मेदारी बन जाती है: जी को कॉल करने वाली प्रत्येक विधि async होनी चाहिए और प्रतीक्षा का भी उपयोग करें। यह एक दिशानिर्देश है, कठोर नियम नहीं है, आप यह तय कर सकते हैं कि आपके विशिष्ट कार्यक्रम में, जी रिटर्न शून्य होना आसान है।

40

मैं इस बारे में बहुत उपयोगी लेख भर आया हूं asyncऔर voidJérôme Laban द्वारा लिखित: https://jaylee.org/archive/2012/07/08/c-sharp-async-tips-and-tricks-part-2-async.void .html

लब्बोलुआब यह है कि async+voidसिस्टम को क्रैश कर सकता है और आमतौर पर केवल यूआई साइड ईवेंट हैंडलर पर उपयोग किया जाना चाहिए।

इसके पीछे का कारण AsyncVoidMethodBuilder द्वारा उपयोग किया गया सिंक्रोनाइज़ेशन संदर्भ है, इस उदाहरण में कोई नहीं है। जब कोई परिवेशी सिंक्रनाइज़ेशन संदर्भ नहीं होता है, तो कोई अपवाद जो किसी async void पद्धति के मुख्य भाग द्वारा अनहेल्ड किया गया है, थ्रेडपूल पर फिर से डाला गया है। हालांकि ऐसा प्रतीत होता है कि कोई अन्य तार्किक स्थान नहीं है जहां उस तरह के अनहेल्ड अपवाद को फेंका जा सकता है, दुर्भाग्यपूर्ण प्रभाव यह है कि इस प्रक्रिया को समाप्त किया जा रहा है, क्योंकि थ्रेडपूल पर अनचाहे अपवाद .NET 2.0 के बाद से प्रभावी रूप से प्रक्रिया को समाप्त कर देते हैं। आप AppDomain.UnhandledException इवेंट का उपयोग करके सभी अखंडित अपवाद को रोक सकते हैं, लेकिन इस घटना से प्रक्रिया को पुनर्प्राप्त करने का कोई तरीका नहीं है।

UI ईवेंट हैंडलर्स लिखते समय, async शून्य विधियाँ किसी भी तरह से दर्द रहित होती हैं क्योंकि अपवादों को गैर-एस्किंस विधियों में पाए जाने वाले तरीके से व्यवहार किया जाता है; उन्हें डिस्पैचर पर फेंक दिया जाता है। इस तरह के अपवादों से उबरने की संभावना है, अधिकांश मामलों के लिए सही होने से अधिक है। यूआई इवेंट हैंडलर के बाहर, हालांकि, async शून्य विधियाँ किसी भी तरह उपयोग करने के लिए खतरनाक हैं और इसे खोजना आसान नहीं है।


क्या वो सही है? मुझे लगा कि टास्क के भीतर एसिंक्स के तरीकों के अपवादों को पकड़ लिया गया है। और भी afaik वहाँ हमेशा एक बहरा करना है SynchronizationContext
NM

30

मुझे इस कथन से स्पष्ट विचार आया।

  1. Async शून्य विधियों में विभिन्न त्रुटि-हैंडलिंग शब्दार्थ हैं। जब किसी अपवाद को किसी Async टास्क या Async टास्क विधि से बाहर फेंक दिया जाता है, तो उस अपवाद को टास्क ऑब्जेक्ट पर पकड़ लिया जाता है और रखा जाता है। Async शून्य विधियों के साथ, कोई टास्क ऑब्जेक्ट नहीं है, इसलिए किसी Async void पद्धति से निकाली गई कोई भी अपवाद सीधे SynchronizationContext (SynchronizationContext एक स्थान का प्रतिनिधित्व करता है) पर उठाया जाएगा "जहाँ" कोड निष्पादित हो सकता है।) जो कि async void पद्धति से सक्रिय था। शुरू कर दिया है

एक Async शून्य विधि से अपवाद कैच के साथ पकड़ा नहीं जा सकता

private async void ThrowExceptionAsync()
{
  throw new InvalidOperationException();
}
public void AsyncVoidExceptions_CannotBeCaughtByCatch()
{
  try
  {
    ThrowExceptionAsync();
  }
  catch (Exception)
  {
    // The exception is never caught here!
    throw;
  }
}

इन अपवादों को AppDomain.UnhandledException या GUI / ASP.NET अनुप्रयोगों के लिए एक समान कैच-ऑल इवेंट का उपयोग करके देखा जा सकता है, लेकिन नियमित अपवाद हैंडलिंग के लिए उन घटनाओं का उपयोग करना अकल्पनीयता के लिए एक नुस्खा है (यह एप्लिकेशन को क्रैश करता है)।

  1. Async शून्य विधियों में विभिन्न कंपोज़िंग शब्दार्थ हैं। टास्क या टास्क को वापस करने वाले Async तरीकों को आसानी से प्रतीक्षा, Task.WhenAny, Task.WhenAll और इसी तरह का उपयोग करके बनाया जा सकता है। Async विधियाँ वापस आ रही हैं वे कॉलिंग कोड को सूचित करने का एक आसान तरीका प्रदान नहीं करती हैं जो उन्होंने पूरा कर लिया है। कई एसिंक्स शून्य विधियों को शुरू करना आसान है, लेकिन जब वे समाप्त हो जाते हैं, तो यह निर्धारित करना आसान नहीं है। Async शून्य विधियाँ उनके SynchronizationContext को सूचित करती हैं जब वे शुरू और खत्म होते हैं, लेकिन नियमित रूप से अनुप्रयोग कोड के लिए एक कस्टम SynchronizationContext एक जटिल समाधान है।

  2. सिंक्रोनस इवेंट हैंडलर का उपयोग करते समय Async शून्य विधि उपयोगी होती है क्योंकि वे अपने अपवादों को सीधे SynchronizationContext पर उठाते हैं, जो तुल्यकालिक ईवेंट हैंडलर के व्यवहार के समान है

अधिक जानकारी के लिए इस लिंक की जाँच करें https://msdn.microsoft.com/en-us/magazine/jj991977.aspx


21

Async शून्य को कॉल करने में समस्या यह है कि आपको कार्य वापस नहीं मिलता है, आपके पास यह जानने का कोई तरीका नहीं है कि फ़ंक्शन का कार्य कब पूरा हुआ है (देखें https://blogs.msdn.microsoft.com/oldnewthing/20170720-00/ ; पी = 96655 )

यहाँ एक async फ़ंक्शन को कॉल करने के तीन तरीके दिए गए हैं:

async Task<T> SomethingAsync() { ... return t; }
async Task SomethingAsync() { ... }
async void SomethingAsync() { ... }

सभी मामलों में, फ़ंक्शन कार्यों की श्रृंखला में बदल जाता है। अंतर यह है कि फ़ंक्शन क्या लौटाता है।

पहले मामले में, फ़ंक्शन एक कार्य देता है जो अंततः टी का उत्पादन करता है।

दूसरे मामले में, फ़ंक्शन एक ऐसा कार्य देता है जिसका कोई उत्पाद नहीं है, लेकिन आप अभी भी इस पर इंतजार कर सकते हैं कि यह कब पूरा होगा।

तीसरा मामला बुरा है। तीसरा केस दूसरे केस की तरह है, सिवाय इसके कि आपको टास्क वापस भी नहीं मिलता। आपके पास यह जानने का कोई तरीका नहीं है कि फ़ंक्शन का कार्य कब पूरा हुआ है।

Async शून्य मामला एक "आग और भूल" है: आप कार्य श्रृंखला शुरू करते हैं, लेकिन आप इसे समाप्त होने पर परवाह नहीं करते हैं। जब फ़ंक्शन वापस आता है, तो आप सभी जानते हैं कि पहली प्रतीक्षा तक सब कुछ निष्पादित हो गया है। पहले इंतजार के बाद सब कुछ भविष्य में कुछ अनिर्दिष्ट बिंदु पर चलेगा जिसकी आपके पास कोई पहुंच नहीं है।


6

मुझे लगता है कि आप async voidपृष्ठभूमि के संचालन को बंद करने के लिए भी उपयोग कर सकते हैं , इसलिए जब तक आप अपवादों को पकड़ने के लिए सावधान रहें। विचार?

class Program {

    static bool isFinished = false;

    static void Main(string[] args) {

        // Kick off the background operation and don't care about when it completes
        BackgroundWork();

        Console.WriteLine("Press enter when you're ready to stop the background operation.");
        Console.ReadLine();
        isFinished = true;
    }

    // Using async void to kickoff a background operation that nobody wants to be notified about when it completes.
    static async void BackgroundWork() {
        // It's important to catch exceptions so we don't crash the appliation.
        try {
            // This operation will end after ten interations or when the app closes. Whichever happens first.
            for (var count = 1; count <= 10 && !isFinished; count++) {
                await Task.Delay(1000);
                Console.WriteLine($"{count} seconds of work elapsed.");
            }
            Console.WriteLine("Background operation came to an end.");
        } catch (Exception x) {
            Console.WriteLine("Caught exception:");
            Console.WriteLine(x.ToString());
        }
    }
}

1
Async शून्य को कॉल करने में समस्या यह है कि आपको कार्य वापस नहीं मिलता है, आपके पास यह जानने का कोई तरीका नहीं है कि फ़ंक्शन का कार्य कब पूरा हुआ है
user8128167

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

एक Async शून्य विधि से अपवाद कैच के साथ नहीं पकड़े जा सकते। msdn.microsoft.com/en-us/magazine/jj991977.aspx
Si Zi

3
@SiZi कैच, एसिंक्राइड शून्य विधि है जैसा कि उदाहरण में दिखाया गया है और यह पकड़ा गया है।
bboyle1234

क्या होगा जब आपकी आवश्यकताओं को बदलने के लिए कोई काम करने की आवश्यकता नहीं है यदि आप इसे लॉग नहीं करते हैं - तो आपको अपने पूरे एपीआई में हस्ताक्षर बदलने होंगे, इसके बजाय केवल तर्क को बदलना होगा जो वर्तमान में // अनदेखा अपवाद है
मिलनी

0

मेरा उत्तर सरल है आप शून्य विधि का इंतजार नहीं कर सकते

Error   CS4008  Cannot await 'void' TestAsync   e:\test\TestAsync\TestAsyncProgram.cs

इसलिए यदि विधि async है तो यह बेहतर है कि आप प्रतीक्षा कर सकें, क्योंकि आप async लाभ को कम कर सकते हैं।


-2

Microsoft प्रलेखन के अनुसार , कभी भी उपयोग नहीं करना चाहिएasync void

ऐसा न करें: निम्न उदाहरण का उपयोग करता है async voidजो पहले अनुरोध तक पहुंचने पर HTTP अनुरोध को पूरा करता है:

  • जो हमेशा ASP.NET कोर एप्स में एक बुरा अभ्यास है।

  • HTTP अनुरोध पूरा होने के बाद HttpResponse तक पहुँचता है।

  • प्रक्रिया को नष्ट कर देता है।

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