यहाँ कोड स्निपेट का अनुक्रम है जो मैंने हाल ही में एसिंक्स सॉल्व्स का उपयोग करके अंतर और विभिन्न समस्याओं का वर्णन करने के लिए किया था।
मान लीजिए कि आपके GUI- आधारित एप्लिकेशन में आपके पास कुछ ईवेंट हैंडलर है जो बहुत समय लेता है, और इसलिए आप इसे अतुल्यकालिक बनाना चाहते हैं। यहां आपके द्वारा शुरू किए गए सिंक्रोनस लॉजिक हैं:
while (true) {
string result = LoadNextItem().Result;
if (result.Contains("target")) {
Counter.Value = result.Length;
break;
}
}
LoadNextItem एक टास्क देता है, जो अंततः आपके द्वारा निरीक्षण करने के लिए कुछ परिणाम देगा। यदि वर्तमान परिणाम वह है जिसे आप ढूंढ रहे हैं, तो आप UI पर कुछ काउंटर के मूल्य को अपडेट करते हैं, और विधि से वापस आते हैं। अन्यथा, आप LoadNextItem से अधिक आइटम संसाधित करना जारी रखते हैं।
अतुल्यकालिक संस्करण के लिए पहला विचार: बस निरंतरता का उपयोग करें! और समय के लिए लूपिंग भाग को अनदेखा करें। मेरा मतलब है, संभवतः क्या गलत हो सकता है?
return LoadNextItem().ContinueWith(t => {
string result = t.Result;
if (result.Contains("target")) {
Counter.Value = result.Length;
}
});
महान, अब हमारे पास एक ऐसा तरीका है जो ब्लॉक नहीं करता है! इसके बजाय दुर्घटना। UI नियंत्रण पर कोई भी अद्यतन UI थ्रेड पर होना चाहिए, इसलिए आपको इसके लिए खाते की आवश्यकता होगी। शुक्र है, यह निर्दिष्ट करने का एक विकल्प है कि निरंतरता कैसे निर्धारित की जानी चाहिए, और इसके लिए एक डिफ़ॉल्ट एक है:
return LoadNextItem().ContinueWith(t => {
string result = t.Result;
if (result.Contains("target")) {
Counter.Value = result.Length;
}
},
TaskScheduler.FromCurrentSynchronizationContext());
महान, अब हमारे पास एक ऐसा तरीका है जो दुर्घटना नहीं करता है! यह इसके बजाय चुपचाप विफल रहता है। निरंतर कार्य अलग-अलग कार्य होते हैं, उनकी स्थिति पूर्ववर्ती कार्य से जुड़ी नहीं होती है। तो भी अगर LoadNextItem दोष, फोन करने वाले को केवल एक कार्य दिखाई देगा जो सफलतापूर्वक पूरा हो गया है। ठीक है, तो बस अपवाद पर गुजरें, अगर कोई है:
return LoadNextItem().ContinueWith(t => {
if (t.Exception != null) {
throw t.Exception.InnerException;
}
string result = t.Result;
if (result.Contains("target")) {
Counter.Value = result.Length;
}
},
TaskScheduler.FromCurrentSynchronizationContext());
महान, अब यह वास्तव में काम करता है। किसी एक आइटम के लिए। अब, उस पाश के बारे में कैसे। बाहर मुड़ता है, मूल तुल्यकालिक संस्करण के तर्क के बराबर एक समाधान कुछ इस तरह दिखाई देगा:
Task AsyncLoop() {
return AsyncLoopTask().ContinueWith(t =>
Counter.Value = t.Result,
TaskScheduler.FromCurrentSynchronizationContext());
}
Task<int> AsyncLoopTask() {
var tcs = new TaskCompletionSource<int>();
DoIteration(tcs);
return tcs.Task;
}
void DoIteration(TaskCompletionSource<int> tcs) {
LoadNextItem().ContinueWith(t => {
if (t.Exception != null) {
tcs.TrySetException(t.Exception.InnerException);
} else if (t.Result.Contains("target")) {
tcs.TrySetResult(t.Result.Length);
} else {
DoIteration(tcs);
}});
}
या, उपरोक्त सभी के बजाय, आप एक ही काम करने के लिए async का उपयोग कर सकते हैं:
async Task AsyncLoop() {
while (true) {
string result = await LoadNextItem();
if (result.Contains("target")) {
Counter.Value = result.Length;
break;
}
}
}
यह अब बहुत अच्छा है, है ना?
Wait
दूसरे उदाहरण में कॉल को हटा दिया है तो दो स्निपेट (अधिकतर) समकक्ष होंगे।