मैं टास्क का उपयोग कब करूँगा। यील्ड ()?


218

मैं async / प्रतीक्षा और Taskबहुत कुछ का उपयोग कर रहा हूं, लेकिन कभी भी उपयोग नहीं कर रहा हूं और Task.Yield()सभी स्पष्टीकरणों के साथ भी ईमानदार होना चाहता हूं मुझे समझ नहीं आता कि मुझे इस पद्धति की आवश्यकता क्यों होगी।

क्या कोई अच्छा उदाहरण दे सकता है जहां Yield()आवश्यकता हो?

जवाबों:


241

जब आप async/ का उपयोग करते हैं await, तो इस बात की कोई गारंटी नहीं है कि जब आप कॉल करते हैं तो विधि await FooAsync()वास्तव में अतुल्यकालिक रूप से चलेगी। आंतरिक कार्यान्वयन पूरी तरह से सिंक्रोनस पथ का उपयोग करके वापस लौटने के लिए स्वतंत्र है।

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

यह भी उपयोगी हो सकता है यदि आप एक अतुल्यकालिक विधि बनाते हैं जिसके लिए कुछ "लंबे समय तक चलने" की आवश्यकता होती है, यानी:

 private async void button_Click(object sender, EventArgs e)
 {
      await Task.Yield(); // Make us async right away

      var data = ExecuteFooOnUIThread(); // This will run on the UI thread at some point later

      await UseDataAsync(data);
 }

Task.Yield()कॉल के बिना , विधि पहले कॉल करने के लिए सभी तरह से तुल्यकालिक रूप से निष्पादित करेगी await


26
मुझे लगता है कि मैं यहाँ कुछ गलत कर रहा हूँ। यदि await Task.Yield()पद्धति को async होने के लिए मजबूर किया जाता है, तो हम "वास्तविक" async कोड लिखने से क्यों परेशान होंगे? एक भारी सिंक विधि की कल्पना करें। यह async, बस को जोड़ने के लिए asyncऔर await Task.Yield()शुरुआत में और जादुई, यह async हो जाएगा? यह बहुत ज्यादा सभी सिंक कोड को लपेटने Task.Run()और नकली async विधि बनाने जैसा होगा।
Krumelur

14
@Krumelur एक बड़ा अंतर है - मेरे उदाहरण को देखो। यदि आप Task.Runइसे लागू करने के लिए a का उपयोग करते हैं, ExecuteFooOnUIThreadतो थ्रेड पूल पर चलेगा, UI थ्रेड पर नहीं। इसके साथ await Task.Yield(), आप इसे इस तरह से अतुल्यकालिक होने के लिए मजबूर करते हैं कि बाद का कोड अभी भी वर्तमान संदर्भ पर चलता है (बस बाद के समय में)। यह कुछ ऐसा नहीं है जिसे आप सामान्य रूप से करेंगे, लेकिन यह अच्छा है कि यदि कोई अजीब कारण के लिए आवश्यक है तो विकल्प है।
रीड कोपसे

7
एक और सवाल: यदि ExecuteFooOnUIThread()बहुत लंबे समय से चल रहा था, तो यह अभी भी यूआई थ्रेड को किसी बिंदु पर लंबे समय तक अवरुद्ध करेगा और यूआई को गैर-जिम्मेदार बना देगा, क्या यह सही है?
क्रुमेलुर

7
@ क्रामेलुर हाँ, यह होगा। बस तुरंत नहीं - यह बाद में होगा।
रीड कोपसे

33
हालांकि यह उत्तर तकनीकी रूप से सही है, यह कथन कि "बाद में शेष कोड निष्पादित होगा" बहुत सार है और भ्रामक हो सकता है। Task.Yield () के बाद कोड का निष्पादन शेड्यूल कंक्रीट SynchronisationContext पर बहुत निर्भर है। और MSDN दस्तावेज़ीकरण स्पष्ट रूप से बताता है कि "अधिकांश UI परिवेशों में UI थ्रेड पर मौजूद सिंक्रनाइज़ेशन संदर्भ अक्सर इनपुट और रेंडरिंग कार्य की तुलना में संदर्भ के लिए पोस्ट किए गए कार्य को प्राथमिकता देगा। इस कारण से, Task.Yield () पर भरोसा न करें। , यूआई उत्तरदायी रखने के लिए। "
विटाली त्वाइयर

36

आंतरिक रूप से, await Task.Yield()बस वर्तमान सिंक्रनाइज़ेशन संदर्भ पर या यादृच्छिक पूल थ्रेड पर निरंतरता को कतार में रखता है, यदि SynchronizationContext.Currentहै null

यह कस्टम वेटर के रूप में कुशलता से लागू किया जाता है। समान प्रभाव पैदा करने वाला कम कुशल कोड इस प्रकार सरल हो सकता है:

var tcs = new TaskCompletionSource<bool>();
var sc = SynchronizationContext.Current;
if (sc != null)
    sc.Post(_ => tcs.SetResult(true), null);
else
    ThreadPool.QueueUserWorkItem(_ => tcs.SetResult(true));
await tcs.Task;

Task.Yield()कुछ अजीब निष्पादन प्रवाह परिवर्तनों के लिए शॉर्ट-कट के रूप में इस्तेमाल किया जा सकता है। उदाहरण के लिए:

async Task DoDialogAsync()
{
    var dialog = new Form();

    Func<Task> showAsync = async () => 
    {
        await Task.Yield();
        dialog.ShowDialog();
    }

    var dialogTask = showAsync();
    await Task.Yield();

    // now we're on the dialog's nested message loop started by dialog.ShowDialog 
    MessageBox.Show("The dialog is visible, click OK to close");
    dialog.Close();

    await dialogTask;
    // we're back to the main message loop  
}

उस ने कहा, मैं ऐसे किसी भी मामले के बारे में नहीं सोच Task.Yield()सकता जहां Task.Factory.StartNewडब्ल्यू / उचित कार्य अनुसूचक के साथ प्रतिस्थापित नहीं किया जा सके।

यह सभी देखें:


आपके उदाहरण में, क्या है और वहां क्या अंतर है var dialogTask = await showAsync();?
एरिक फिलिप्स

@ ErikPhilips, var dialogTask = await showAsync()संकलित नहीं करेगा क्योंकि await showAsync()अभिव्यक्ति वापस नहीं आती है Task(इसके विपरीत यह बिना करता है await)। उन्होंने कहा, यदि आप करते हैं await showAsync(), तो संवाद के बंद होने के बाद ही इसे फिर से शुरू किया जाएगा, यही कारण है कि यह अलग है। ऐसा इसलिए window.ShowDialogहै क्योंकि यह एक तुल्यकालिक एपीआई है (इसके बावजूद यह अभी भी संदेशों को पंप करता है)। उस कोड में, मैं जारी रखना चाहता था जबकि संवाद अभी भी दिखाया गया है।
noseratio

5

एक का उपयोग Task.Yield()async पुनरावृत्ति करते समय एक स्टैक अतिप्रवाह को रोकने के लिए है। Task.Yield()तुल्यकालिक निरंतरता को रोकता है। ध्यान दें, हालांकि, यह एक OutOfMemory अपवाद पैदा कर सकता है (जैसा कि ट्रायंको द्वारा नोट किया गया है)। अंतहीन पुनरावृत्ति अभी भी सुरक्षित नहीं है और आप शायद लूप के रूप में पुनरावृत्ति को फिर से लिखना बंद कर सकते हैं।

private static void Main()
    {
        RecursiveMethod().Wait();
    }

    private static async Task RecursiveMethod()
    {
        await Task.Delay(1);
        //await Task.Yield(); // Uncomment this line to prevent stackoverlfow.
        await RecursiveMethod();
    }

4
यह एक स्टैक अतिप्रवाह को रोक सकता है, लेकिन यह अंततः सिस्टम मेमोरी से बाहर चला जाएगा यदि आप इसे लंबे समय तक चलने देते हैं। प्रत्येक पुनरावृत्ति एक नया कार्य बनाता है जो कभी पूरा नहीं होता है, क्योंकि बाहरी कार्य एक आंतरिक कार्य की प्रतीक्षा कर रहा है, जिसे अभी तक एक अन्य आंतरिक कार्य की प्रतीक्षा है, और इसी तरह। यह ठीक नहीं है। वैकल्पिक रूप से, आपके पास बस एक सबसे बाहरी कार्य हो सकता है जो कभी पूरा नहीं होता है, और बस इसे पुनरावृत्ति के बजाय लूप है। कार्य कभी पूरा नहीं होगा, लेकिन उनमें से केवल एक ही होगा। लूप के अंदर, यह आपके द्वारा पसंद की जाने वाली किसी भी चीज़ की उपज या प्रतीक्षा कर सकता है।
त्रियुन्को

मैं स्टैक ओवरफ़्लो को पुन: उत्पन्न नहीं कर सकता। ऐसा लगता है कि await Task.Delay(1)इसे रोकने के लिए पर्याप्त है। (कंसोल ऐप, .NET कोर 3.1, सी # 8)
थियोडोर ज़ूलियास

-8

Task.Yield() async विधियों के नकली कार्यान्वयन में उपयोग किया जा सकता है।


4
आपको कुछ विवरण प्रदान करना चाहिए।
PJProudhon

3
इस प्रयोजन के लिए, मैं टास्क का उपयोग करना चाहूंगा। उच्च स्तरीय - अधिक विचार के लिए इस msdn ब्लॉग पोस्ट में अनुभाग टास्क देखें ।CompletedTask ।
ग्रेज़गोरज़ स्मुल्को

2
Task.CompletedTask, या Task.FromResult का उपयोग करने के साथ समस्या यह है कि आप बग को याद कर सकते हैं जो केवल तब प्रकट होता है जब विधि अतुल्यकालिक निष्पादित करती है।
जोकिम एमएच
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.