2016-01-26 20 views
48

मैं एक मौजूदा कार्यक्रम संशोधित करने की आवश्यकता है और यह कोड निम्नलिखित शामिल हैं:Async LINQ में इंतजार चयन

var inputs = events.Select(async ev => await ProcessEventAsync(ev)) 
        .Select(t => t.Result) 
        .Where(i => i != null) 
        .ToList(); 

लेकिन यह मेरे लिए बहुत अजीब, चयन में async और await के सभी प्रयोग के पहले लगता है। स्टीफन क्लेरी द्वारा this answer के अनुसार मुझे उनको छोड़ने में सक्षम होना चाहिए।

फिर दूसरा Select जो परिणाम का चयन करता है। इसका मतलब यह नहीं है कि कार्य बिल्कुल असीमित नहीं है और सिंक्रनाइज़ किया जाता है (कुछ भी करने के लिए इतना प्रयास नहीं), या कार्य को असीमित रूप से निष्पादित किया जाएगा और जब यह किया जाता है तो शेष क्वेरी निष्पादित की जाती है?

मैं निम्नलिखित की तरह ऊपर कोड लिखने चाहिए another answer by Stephen Cleary के अनुसार:

var tasks = await Task.WhenAll(events.Select(ev => ProcessEventAsync(ev))); 
var inputs = tasks.Where(result => result != null).ToList(); 

और यह पूरी तरह से इस तरह एक ही है?

var inputs = (await Task.WhenAll(events.Select(ev => ProcessEventAsync(ev)))) 
             .Where(result => result != null).ToList(); 

जबकि मैं इस परियोजना पर काम कर रहा हूँ मैं पहली बार कोड नमूना बदलना चाहते हैं, लेकिन मैं (जाहिरा तौर पर काम कर रहे) async कोड बदलने पर भी उत्सुक नहीं हूँ। हो सकता है कि मैं बस कुछ भी नहीं कर रहा हूं और सभी 3 कोड नमूने बिल्कुल वही काम करते हैं?

ProcessEventsAsync इस तरह दिखता है:

async Task<InputResult> ProcessEventAsync(InputEvent ev) {...} 
+0

ProceesEventAsync का रिटर्न प्रकार क्या है? – tede24

+0

@ tede24 यह 'कार्य ' है 'इनपुट रेसल्ट' एक कस्टम क्लास है। –

+0

आपकी राय में आपके संस्करण पढ़ने के लिए बहुत आसान हैं। हालांकि, आप अपने 'कहां' से पहले कार्यों से परिणामों का चयन 'भूल गए हैं। – Max

उत्तर

48
var inputs = events.Select(async ev => await ProcessEventAsync(ev)) 
        .Select(t => t.Result) 
        .Where(i => i != null) 
        .ToList(); 

लेकिन इस async के सभी उपयोग की मेरे लिए बहुत अजीब लगता है, पहली और चयन में प्रतीक्षा करें। स्टीफन क्लेरी द्वारा इस जवाब के अनुसार मुझे उनको छोड़ने में सक्षम होना चाहिए।

Select पर कॉल मान्य है। इन दो पंक्तियों को अनिवार्य रूप से समान हैं:

events.Select(async ev => await ProcessEventAsync(ev)) 
events.Select(ev => ProcessEventAsync(ev)) 

(वहाँ एक छोटी सी के बारे में कैसे एक तुल्यकालिक अपवाद ProcessEventAsync से फेंका किया जाएगा अंतर है, लेकिन इस कोड के संदर्भ में यह बात बिल्कुल भी नहीं है।)

फिर दूसरा चयन जो परिणाम का चयन करता है। इसका मतलब यह नहीं है कि कार्य बिल्कुल असीमित नहीं है और सिंक्रनाइज़ किया जाता है (कुछ भी करने के लिए इतना प्रयास नहीं), या कार्य को असीमित रूप से निष्पादित किया जाएगा और जब यह किया जाता है तो शेष क्वेरी निष्पादित की जाती है?

इसका मतलब है कि क्वेरी अवरुद्ध है। तो यह वास्तव में असीमित नहीं है।

यह तोड़कर नीचे:

var inputs = events.Select(async ev => await ProcessEventAsync(ev)) 

पहले प्रत्येक घटना के लिए एक अतुल्यकालिक आपरेशन शुरू कर देंगे। तो फिर इस लाइन:

    .Select(t => t.Result) 

एक समय में एक पूरा करने के लिए उन कार्यों के लिए इंतजार करेंगे (पहले यह पहली घटना के ऑपरेशन के लिए तो अगले, तो अगला, आदि इंतजार कर रहा है,)।

यह वह हिस्सा है जिसकी मुझे परवाह नहीं है, क्योंकि यह ब्लॉक करता है और AggregateException में किसी भी अपवाद को लपेटता है।

और क्या यह पूरी तरह से ऐसा ही है?

var tasks = await Task.WhenAll(events.Select(ev => ProcessEventAsync(ev))); 
var inputs = tasks.Where(result => result != null).ToList(); 

var inputs = (await Task.WhenAll(events.Select(ev => ProcessEventAsync(ev)))) 
             .Where(result => result != null).ToList(); 

हाँ, उन दो उदाहरण बराबर हैं। वे दोनों सभी एसिंक्रोनस ऑपरेशंस (events.Select(...)) शुरू करते हैं, फिर किसी भी क्रम (await Task.WhenAll(...)) में पूरा करने के लिए सभी परिचालनों के लिए असीमित रूप से प्रतीक्षा करें, फिर शेष कार्य (Where...) के साथ आगे बढ़ें।

इनमें से दोनों उदाहरण मूल कोड से अलग हैं। मूल कोड अवरुद्ध है और AggregateException में अपवाद लपेट जाएगा।

+0

इसे साफ़ करने के लिए चीयर्स! तो 'एग्रीगेट एक्सेप्शन' में लिपटे अपवादों के बजाय मुझे दूसरे कोड में कई अलग-अलग अपवाद मिलेगा? –

+1

@AlexanderDerck: नहीं, पुराने और नए कोड दोनों में, केवल पहला अपवाद उठाया जाएगा। लेकिन 'परिणाम' के साथ इसे 'समेकित अपवाद' में लपेटा जाएगा। –

12

मौजूदा कोड काम कर रहा है, लेकिन धागा ब्लॉक कर रहा है।

.Select(async ev => await ProcessEventAsync(ev)) 

हर घटना के लिए एक नया कार्य बनाता है, लेकिन

.Select(t => t.Result) 

ब्लॉक धागा प्रत्येक नया कार्य समाप्त होने की प्रतीक्षा।

दूसरी तरफ आपका कोड एक ही परिणाम उत्पन्न करता है लेकिन असीमित रहता है।

आपके पहले कोड पर सिर्फ एक टिप्पणी। यह लाइन

var tasks = await Task.WhenAll(events... 

एक ही कार्य का उत्पादन करेगा ताकि परिवर्तक को एकवचन में नामित किया जाना चाहिए।

अंत में अपने पिछले कोड एक ही है, लेकिन संदर्भ के लिए अधिक संक्षिप्त

है: Task.Wait/Task.WhenAll

+0

का मूल्यांकन शुरू करता है तो पहला कोड ब्लॉक वास्तव में सिंक्रनाइज़ निष्पादित किया जाता है? –

+1

हां, क्योंकि परिणाम तक पहुंचने से प्रतीक्षा होती है जो धागे को अवरुद्ध करता है। दूसरी ओर जब एक नया कार्य पैदा करता है तो आप इसका इंतजार कर सकते हैं। – tede24

+1

इस प्रश्न पर वापस आकर 'कार्य' चर के नाम के बारे में आपकी टिप्पणी को देखते हुए, आप पूरी तरह से सही हैं। भयानक पसंद, वे कार्य भी नहीं कर रहे हैं क्योंकि वे तुरंत इंतजार कर रहे हैं। मैं सिर्फ प्रश्न छोड़ दूंगा जैसा कि –

2

Linq में उपलब्ध मौजूदा तरीकों के साथ यह काफी बदसूरत दिखता है:

 var tasks = items.Select(
      async item => new 
      { 
       Item = item, 
       IsValid = await IsValid(item) 
      }); 
     var tuples = await Task.WhenAll(tasks); 
     var validItems = tuples 
      .Where(p => p.IsValid) 
      .Select(p => p.Item) 
      .ToList(); 

नेट की उम्मीद है कि निम्नलिखित संस्करणों कार्यों और संग्रह का कार्य के संग्रह को संभालने के लिए और अधिक सुरुचिपूर्ण टूलींग के साथ आ जाएगा।

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