जवाबों:
जब आप 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
।
Task.Run
इसे लागू करने के लिए a का उपयोग करते हैं, ExecuteFooOnUIThread
तो थ्रेड पूल पर चलेगा, UI थ्रेड पर नहीं। इसके साथ await Task.Yield()
, आप इसे इस तरह से अतुल्यकालिक होने के लिए मजबूर करते हैं कि बाद का कोड अभी भी वर्तमान संदर्भ पर चलता है (बस बाद के समय में)। यह कुछ ऐसा नहीं है जिसे आप सामान्य रूप से करेंगे, लेकिन यह अच्छा है कि यदि कोई अजीब कारण के लिए आवश्यक है तो विकल्प है।
ExecuteFooOnUIThread()
बहुत लंबे समय से चल रहा था, तो यह अभी भी यूआई थ्रेड को किसी बिंदु पर लंबे समय तक अवरुद्ध करेगा और यूआई को गैर-जिम्मेदार बना देगा, क्या यह सही है?
आंतरिक रूप से, 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();
?
var dialogTask = await showAsync()
संकलित नहीं करेगा क्योंकि await showAsync()
अभिव्यक्ति वापस नहीं आती है Task
(इसके विपरीत यह बिना करता है await
)। उन्होंने कहा, यदि आप करते हैं await showAsync()
, तो संवाद के बंद होने के बाद ही इसे फिर से शुरू किया जाएगा, यही कारण है कि यह अलग है। ऐसा इसलिए window.ShowDialog
है क्योंकि यह एक तुल्यकालिक एपीआई है (इसके बावजूद यह अभी भी संदेशों को पंप करता है)। उस कोड में, मैं जारी रखना चाहता था जबकि संवाद अभी भी दिखाया गया है।
एक का उपयोग 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();
}
await Task.Delay(1)
इसे रोकने के लिए पर्याप्त है। (कंसोल ऐप, .NET कोर 3.1, सी # 8)
Task.Yield()
async विधियों के नकली कार्यान्वयन में उपयोग किया जा सकता है।
await Task.Yield()
पद्धति को async होने के लिए मजबूर किया जाता है, तो हम "वास्तविक" async कोड लिखने से क्यों परेशान होंगे? एक भारी सिंक विधि की कल्पना करें। यह async, बस को जोड़ने के लिएasync
औरawait Task.Yield()
शुरुआत में और जादुई, यह async हो जाएगा? यह बहुत ज्यादा सभी सिंक कोड को लपेटनेTask.Run()
और नकली async विधि बनाने जैसा होगा।