8

एक कार्य चर है और कहता है कि कार्य अभी चल रहा है .. निम्न पंक्ति को निष्पादित करके।क्या होता है यदि मैं किसी ऐसे कार्य का इंतजार करता हूं जो पहले से चल रहा है या दौड़ रहा है?

await _task; 

मैं सोच रहा था क्या होता है जब मैं इस कोड लिखने:

await _task; 
await _task; 

यह कार्य दो बार अमल होगा? या एक अपवाद फेंक क्योंकि यह पहले से ही चल रहा है?

+4

आप इसे क्यों नहीं देखते? परीक्षण करना मुश्किल नहीं है। – Enigmativity

+0

यहां होने की मुख्य अंतर्दृष्टि यह है कि * प्रतीक्षा एक कार्य * नहीं चलाती है। प्रतीक्षा करें * असीमित रूप से कार्य समाप्त होने की प्रतीक्षा करता है *। इसलिए नाम "प्रतीक्षा" है। एक निरंतर मिथक है कि एक कार्य शुरू करने का इंतजार है, लेकिन निश्चित रूप से यह नहीं है; जब तक आप इसका इंतजार कर रहे हों तब तक आपके पास पहले से ही एक प्रारंभिक कार्य है। –

+0

मेरे लिए असली आंख खोलने वाला 'निरंतरता' की अवधारणा थी। मुझे लगता है कि उस अंतर्दृष्टि के बिना, 'प्रतीक्षा' समझना मुश्किल है। – Sentinel

उत्तर

9

क्या यह कार्य दो बार निष्पादित करेगा? या एक अपवाद फेंक दें क्योंकि इसमें पहले से चल रहा है?

नहीं और नहीं। एकमात्र चीज await कॉल Task.GetAwaiter है, यह किसी भी चीज़ को चलाने का कारण नहीं बनती है। यदि कार्य पूरा होने के लिए पहले से ही चलाया गया है, तो यह Task<T> है, या तो Task है, तो अगली पंक्ति में सिंक्रनाइज़ रूप से चलाएं, क्योंकि पहले से ही पूरा किए गए कार्यों के लिए अनुकूलन है।

एक साधारण डेमो:

async Task Main() 
{ 
    var foo = FooAsync(); 
    await foo; 
    await foo; 

    var bar = BarAsync(); 
    var firstResult = await bar; 
    var secondResult = await bar; 

    Console.WriteLine(firstResult); 
    Console.WriteLine(secondResult); 
} 

public async Task FooAsync() 
{ 
    await Task.Delay(1); 
} 

public async Task<int> BarAsync() 
{ 
    await Task.Delay(1); 
    return 1; 
} 

आप राज्य मशीन ही थोड़ा गहराई में जाकर, तो आप इस देखेंगे:

this.<foo>5__1 = this.<>4__this.FooAsync(); 
taskAwaiter = this.<foo>5__1.GetAwaiter(); 
if (!taskAwaiter.IsCompleted) 
{ 
    this.<>1__state = 0; 
    this.<>u__1 = taskAwaiter; 
    M.<FooBar>d__0 <FooBar>d__ = this; 
    this.<>t__builder.AwaitUnsafeOnCompleted<TaskAwaiter, M.<FooBar>d__0> 
                (ref taskAwaiter, ref <FooBar>d__); 
    return; 
} 

यह अनुकूलन पहला काम के पूरा होने की जांच करता है। यदि कार्य पूरा नहीं हुआ है, तो यह UnsafeOnCompleted पर कॉल करेगा जो निरंतरता पंजीकृत करेगा। यह पूरा हो गया है, तो यह switch टूट जाता है और करने के लिए चला जाता है:

this.<>1__state = -2; 
this.<>t__builder.SetResult(); 

कौन सा अंतर्निहित Task के लिए परिणाम सेट करता है, और इस तरह से आप वास्तव में तुल्यकालिक मूल्य मिलता है।

var i = 0; 
var task = Task.Run(() => { i++; Console.WriteLine(i); return i; }); 

var x = await task; 
var y = await task; 

Console.WriteLine(x); 
Console.WriteLine(y); 

यह लिखते हैं::

1 
1 
1 

तो, स्पष्ट रूप से, काम केवल एक बार चलाता है

+4

और यह ध्यान दिया जाना चाहिए कि * यह एक अच्छी बात है *। एसिंक्रोनस कोड के बारे में मुश्किल भागों में से एक यह है कि अधिकांश एसिंक्रोनस ऑपरेशंस वास्तव में सिंक्रनाइज़ रूप से समाप्त हो सकते हैं - और यह एक अलग मामला है जिसे आपको संभालना है, क्योंकि उस मामले में आपके एसिंक्रोनस कॉलबैक को * नहीं कहा जा रहा है। 'प्रतीक्षा' आपके द्वारा इस जटिलता को छुपाता है, और यह आपको दस अलग-अलग स्थानों में एक ही कार्य का इंतजार करने की अनुमति देता है (उदाहरण के लिए बहुत उपयोगी है जैसे अतुल्यकालिक आलसी प्रारंभिक)। – Luaan

+0

क्या मुझे आश्वस्त किया जा सकता है कि कार्य द्वारा इंगित विधि को दो बार निष्पादित नहीं किया जाएगा, भले ही कार्य चल रहा है या पहले से चल रहा है? –

+0

@ बिलालफज़लानी हां, आप कर सकते हैं। यदि 'कार्य' पहले ही पूरा हो चुका है, तो यह परिणाम को केवल अनदेखा कर देगा। –

6

मैं एक साथ इस त्वरित परीक्षण फेंक दिया।

+0

धन्यवाद @ एनिग्मैटीविटी। वह वास्तव में सहायक था –

संबंधित मुद्दे