कल मैं नए C # "async" फ़ीचर के बारे में बात कर रहा था, विशेष रूप से इस बात पर कि क्या उत्पन्न कोड जैसा दिख रहा है, और the GetAwaiter()
/ BeginAwait()
/ EndAwait()
कॉल।
हमने C # कंपाइलर द्वारा उत्पन्न राज्य मशीन में कुछ विस्तार से देखा, और दो पहलू थे जिन्हें हम समझ नहीं पाए:
- क्यों उत्पन्न वर्ग में एक
Dispose()
विधि और एक$__disposing
चर होता है, जिसका उपयोग कभी नहीं होता है (और वर्ग लागू नहीं होता हैIDisposable
)। - आंतरिक
state
चर को किसी भी कॉल से पहले 0 पर सेट क्यों किया जाता हैEndAwait()
, जब 0 सामान्य रूप से "यह प्रारंभिक प्रवेश बिंदु है" का अर्थ प्रतीत होता है।
मुझे संदेह है कि पहले बिंदु का जवाब कुछ और दिलचस्प तरीके से दिया जा सकता है, क्योंकि अगर किसी के पास कोई और जानकारी हो तो उसे सुनकर मुझे खुशी होगी। हालांकि यह सवाल दूसरे बिंदु के बारे में अधिक है।
यहाँ नमूना कोड का एक बहुत ही सरल टुकड़ा है:
using System.Threading.Tasks;
class Test
{
static async Task<int> Sum(Task<int> t1, Task<int> t2)
{
return await t1 + await t2;
}
}
... और यहां वह कोड है जो उस MoveNext()
विधि के लिए उत्पन्न होता है जो राज्य मशीन को लागू करता है। इसे सीधे प्रतिक्षेपक से कॉपी किया जाता है - मैंने अकथ्य चर नाम तय नहीं किए हैं:
public void MoveNext()
{
try
{
this.$__doFinallyBodies = true;
switch (this.<>1__state)
{
case 1:
break;
case 2:
goto Label_00DA;
case -1:
return;
default:
this.<a1>t__$await2 = this.t1.GetAwaiter<int>();
this.<>1__state = 1;
this.$__doFinallyBodies = false;
if (this.<a1>t__$await2.BeginAwait(this.MoveNextDelegate))
{
return;
}
this.$__doFinallyBodies = true;
break;
}
this.<>1__state = 0;
this.<1>t__$await1 = this.<a1>t__$await2.EndAwait();
this.<a2>t__$await4 = this.t2.GetAwaiter<int>();
this.<>1__state = 2;
this.$__doFinallyBodies = false;
if (this.<a2>t__$await4.BeginAwait(this.MoveNextDelegate))
{
return;
}
this.$__doFinallyBodies = true;
Label_00DA:
this.<>1__state = 0;
this.<2>t__$await3 = this.<a2>t__$await4.EndAwait();
this.<>1__state = -1;
this.$builder.SetResult(this.<1>t__$await1 + this.<2>t__$await3);
}
catch (Exception exception)
{
this.<>1__state = -1;
this.$builder.SetException(exception);
}
}
यह लंबा है, लेकिन इस प्रश्न के लिए महत्वपूर्ण लाइनें ये हैं:
// End of awaiting t1
this.<>1__state = 0;
this.<1>t__$await1 = this.<a1>t__$await2.EndAwait();
// End of awaiting t2
this.<>1__state = 0;
this.<2>t__$await3 = this.<a2>t__$await4.EndAwait();
दोनों मामलों में राज्य को बाद में फिर से बदल दिया जाता है इससे पहले कि यह स्पष्ट रूप से मनाया जाता है ... तो इसे 0 पर क्यों सेट करें? यदि MoveNext()
इस बिंदु पर फिर से बुलाया गया (सीधे या इसके माध्यम से Dispose
) तो यह प्रभावी रूप से async विधि को फिर से शुरू करेगा, जो पूरी तरह से अनुपयुक्त होगा जहां तक मैं बता सकता हूं ... अगर और MoveNext()
नहीं बुलाया गया है, तो राज्य में परिवर्तन अप्रासंगिक है।
क्या यह बस कंपाइलर पुन: उपयोग करने वाले कंपाइलर ब्लॉक जेनरेशन कोड का एक साइड-इफ़ेक्ट है async के लिए, जहाँ इसका अधिक स्पष्ट स्पष्टीकरण हो सकता है?
महत्वपूर्ण अस्वीकरण
जाहिर है कि यह सिर्फ CTP कंपाइलर है। मैं पूरी तरह से चीजों को अंतिम रिलीज से पहले बदलने की उम्मीद करता हूं - और संभवतः अगले सीटीपी रिलीज से पहले भी। यह प्रश्न किसी भी तरह से यह दावा करने की कोशिश नहीं कर रहा है कि यह C # संकलक या उस जैसी किसी चीज़ में कोई दोष है। मैं सिर्फ यह जानने की कोशिश कर रहा हूं कि क्या इसके लिए कोई सूक्ष्म कारण है कि मैं चूक गया हूं :)