टास्क का इंतजार करें। क्या - डेडलॉक?


194

मैं काफी Task.Waitऔर के बीच का अंतर नहीं समझताawait

मैं ASP.NET WebAPI सेवा में निम्नलिखित कार्यों के समान है:

public class TestController : ApiController
{
    public static async Task<string> Foo()
    {
        await Task.Delay(1).ConfigureAwait(false);
        return "";
    }

    public async static Task<string> Bar()
    {
        return await Foo();
    }

    public async static Task<string> Ros()
    {
        return await Bar();
    }

    // GET api/test
    public IEnumerable<string> Get()
    {
        Task.WaitAll(Enumerable.Range(0, 10).Select(x => Ros()).ToArray());

        return new string[] { "value1", "value2" }; // This will never execute
    }
}

Getगतिरोध कहां होगा?

इसका क्या कारण हो सकता है? जब मैं इसके बजाय अवरुद्ध प्रतीक्षा का उपयोग करता हूं तो यह समस्या क्यों नहीं है await Task.Delay?


@ सरवाइव: मेरे पास समय रहते ही मैं रेपो लेकर वापस आ जाऊंगा। अभी के लिए यह काम करता है Task.Delay(1).Wait()जिसके साथ काफी अच्छा है।
रोनग

2
Task.Delay(1).Wait()मूल रूप से के रूप में सटीक एक ही बात है Thread.Sleep(1000)। वास्तविक उत्पादन कोड में यह शायद ही उचित है।
सर्विस

@ क्रोनोग: आपका WaitAllगतिरोध पैदा हो रहा है। अधिक विवरण के लिए मेरे उत्तर में मेरे ब्लॉग का लिंक देखें। आपको await Task.WhenAllइसके बजाय उपयोग करना चाहिए ।
स्टीफन क्लीयर

6
@ronag क्योंकि आपके पास ConfigureAwait(false)एक एकल कॉल है Barया Rosगतिरोध नहीं है, लेकिन क्योंकि आपके पास एक एन्यूमरेबल है जो एक से अधिक निर्माण कर रहा है और फिर उन सभी पर प्रतीक्षा कर रहा है, पहली बार दूसरी गतिरोध करेगा। यदि आप await Task.WhenAllसभी कार्यों पर प्रतीक्षा करने के बजाय, ताकि आप एएसपी संदर्भ को अवरुद्ध न करें, तो आप विधि को सामान्य रूप से वापस देखेंगे।
सर्विक्स

2
@ क्रोनग आपके अन्य विकल्प .ConfigureAwait(false) पेड़ को ब्लॉक करने तक सभी तरह से जोड़ना होगा , इस तरह कुछ भी कभी भी मुख्य संदर्भ में वापस लाने की कोशिश नहीं कर रहा है; इससे काम बन जाएगा। एक अन्य विकल्प एक आंतरिक सिंक्रनाइज़ेशन संदर्भ को स्पिन करना होगा। लिंक करें । आप डाल दिया Task.WhenAllएक में AsyncPump.Runयह प्रभावी रूप से आप के लिए जरूरत के बिना पूरी बात पर अवरुद्ध कर देगा ConfigureAwaitकहीं भी, लेकिन शायद एक overly-जटिल समाधान है कि।
सर्व करें

जवाबों:


268

Waitऔर await- जबकि वैचारिक रूप से समान - वास्तव में पूरी तरह से अलग हैं।

Waitजब तक टास्क पूरा नहीं हो जाता है, तब तक ब्लॉक करेगा। तो वर्तमान धागा वस्तुतः अवरुद्ध है कार्य पूरा होने की प्रतीक्षा में। एक सामान्य नियम के रूप में, आपको " asyncसभी तरह से नीचे" का उपयोग करना चाहिए ; यह है, asyncकोड पर ब्लॉक नहीं है । अपने ब्लॉग पर, मैं इस विवरण में जाता हूं कि कैसे एसिंक्रोनस कोड में अवरोधन गतिरोध का कारण बनता है

awaitजब तक कार्य पूरा नहीं हो जाता तब तक अतुलनीय रूप से प्रतीक्षा करेंगे। इसका मतलब है कि वर्तमान पद्धति "रुकी हुई" है (इसका राज्य कैप्चर किया गया है) और यह विधि अपने कॉलर को एक अधूरा काम देती है। बाद में, जब awaitअभिव्यक्ति पूरी हो जाती है, तो शेष विधि एक निरंतरता के रूप में निर्धारित की जाती है।

आपने एक "कोऑपरेटिव ब्लॉक" का भी उल्लेख किया है, जिसके द्वारा मुझे लगता है कि आप एक कार्य का मतलब है कि आप Waitवेटिंग थ्रेड पर निष्पादित कर सकते हैं। ऐसी स्थितियां हैं जहां यह हो सकता है, लेकिन यह एक अनुकूलन है। ऐसी कई स्थितियाँ हैं जहाँ यह नहीं हो सकता है, जैसे कि यदि कार्य किसी अन्य अनुसूचक के लिए है, या यदि यह पहले से ही शुरू है या यदि यह एक गैर-कोड कार्य है (जैसे कि आपके कोड उदाहरण में: Waitनिष्पादित नहीं कर सकताDelay कार्य इनलाइन क्योंकि कोई कोड नहीं है इसके लिए)।

आपको मेरा async/ awaitइंट्रो मददगार लग सकता है।


4
मुझे लगता है कि एक गलतफहमी है, Waitठीक awaitगतिरोध काम करता है ।
रॉनग ऑक्ट

5
नहीं, कार्य अनुसूचक ऐसा नहीं करेगा। Waitथ्रेड को ब्लॉक करता है, और इसका उपयोग अन्य चीजों के लिए नहीं किया जा सकता है।
स्टीफन क्लीयर

8
@ronag मेरा अनुमान है कि आपको बस अपने तरीके के नाम मिलाने हैं और आपका गतिरोध वास्तव में अवरोधक कोड के कारण हुआ और कोड के साथ काम किया await। या तो वह, या गतिरोध या तो असंबंधित था और आपने समस्या का गलत निदान किया।
सर्व करें

3
@hexterminator: यह डिज़ाइन द्वारा है - यह UI ऐप्स के लिए बहुत अच्छा काम करता है, लेकिन ASP.NET ऐप के लिए रास्ते में है। ASP.NET Core ने इसे हटाकर निश्चित कर दिया है SynchronizationContext, इसलिए ASP.NET Core अनुरोध के भीतर अवरुद्ध करना अब गतिरोध नहीं है।
स्टीफन क्ली

1
यहां msdn पर, यह कहता है कि प्रतीक्षा अलग-अलग थ्रेड पर अतुल्यकालिक रूप से चलती है। क्या मैं कुछ भूल रहा हूँ? msdn.microsoft.com/en-us/library/hh195051(v=vs.110).aspx
batmaci

6

विभिन्न स्रोतों से जो मैंने पढ़ा, उसके आधार पर:

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

एकल taskके पूरा होने की प्रतीक्षा करने के लिए , आप इसकी Task.Waitविधि कह सकते हैं । करने के लिए एक कॉल Waitविधि ब्लॉक बुला धागा एकल वर्ग उदाहरण जब तक निष्पादन पूरा कर लिया है। Wait()किसी कार्य के पूरा होने तक बिना शर्त प्रतीक्षा करने के लिए पैरामीटर रहित विधि का उपयोग किया जाता है। कार्य Thread.Sleepदो सेकंड के लिए सोने के लिए विधि को बुलाकर काम का अनुकरण करता है ।

यह लेख भी एक अच्छा पढ़ा है।


3
"क्या यह तकनीकी रूप से गलत नहीं है? क्या कोई स्पष्ट कर सकता है?" - क्या मैं स्पष्ट कर सकता हूं; क्या आप यह सवाल पूछ रहे हैं? (मैं अभी स्पष्ट करना चाहता हूं कि क्या आप बनाम जवाब दे रहे हैं)। यदि आप पूछ रहे हैं: यह एक अलग प्रश्न के रूप में बेहतर काम कर सकता है; यह एक जवाब के रूप यहाँ नया प्रतिक्रिया प्राप्त की संभावना नहीं है
मार्क Gravell

1
मैंने इस सवाल का जवाब दिया है और इस प्रश्न के लिए एक अलग प्रश्न पूछा है जो मैंने यहां stackoverflow.com/questions/53654006// थैंक्स @MarcGravell। क्या आप अब उत्तर के लिए अपना विलोपन वोट हटा सकते हैं?
आयुष्मती

"क्या आप अब जवाब के लिए अपने विलोपन वोट को हटा सकते हैं?" - वह मेरा नहीं है; ♦ के लिए धन्यवाद, मेरे द्वारा इस तरह के किसी भी वोट ने तुरंत प्रभाव डाला होगा। हालाँकि, मुझे नहीं लगता कि यह प्रश्न के प्रमुख बिंदुओं का उत्तर देता है, जो कि गतिरोध के व्यवहार के बारे में है।
मार्क Gravell

-2

कुछ महत्वपूर्ण तथ्यों को अन्य उत्तरों में नहीं दिया गया था:

CIL स्तर पर "async प्रतीक्षा" अधिक जटिल है और इस प्रकार मेमोरी और CPU समय खर्च होता है।

यदि प्रतीक्षा समय अस्वीकार्य है तो किसी भी कार्य को रद्द किया जा सकता है।

"Async प्रतीक्षा" के मामले में हमारे पास इसे रद्द करने या इसकी निगरानी करने के लिए इस तरह के कार्य के लिए हैंडलर नहीं है।

टास्क का उपयोग करना अधिक लचीला है, फिर "async प्रतीक्षा"।

किसी भी सिंक कार्यक्षमता को एसिंक्स द्वारा लपेटा जा सकता है।

public async Task<ActionResult> DoAsync(long id) 
{ 
    return await Task.Run(() => { return DoSync(id); } ); 
} 

मैं यह नहीं देखता कि मुझे कोड डुप्लिकेट फ़ोट सिंक और एस्किंस विधि या हैक का उपयोग करके क्यों रहना चाहिए।

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