2013-08-06 9 views
5

मेरी MVVM आवेदन मेरे विचार मॉडल 3 अलग सेवा तरीकों कहता है, में एक आम प्रारूप में प्रत्येक से डेटा धर्मान्तरित और फिर सेवा परत में प्रत्येक विधि एक नया शुरू होता है संपत्ति अधिसूचना/नमूदार संग्रह आदिUI थ्रेड को अवरोधित किए बिना एकाधिक कार्य के बाद मैं कैसे जारी रखूं?

का उपयोग कर यूआई अद्यतन करता है Task और दृश्य मॉडल में Task देता है। यहां मेरी सेवा विधियों में से एक का उदाहरण दिया गया है।

public class ResourceService 
{ 
internal static Task LoadResources(Action<IEnumerable<Resource>> completedCallback, Action<Exception> errorCallback) 
{ 
    var t = Task.Factory.StartNew(() => 
    { 
     //... get resources from somewhere 
     return resources; 
    }); 

    t.ContinueWith(task => 
    { 
     if (task.IsFaulted) 
     { 
      errorCallback(task.Exception); 
      return; 
     } 
     completedCallback(task.Result); 
    }, TaskScheduler.FromCurrentSynchronizationContext()); 

    return t; 
} 
} 

यहाँ बुला कोड और देखने मॉडल के अन्य प्रासंगिक भागों है ...

private ObservableCollection<DataItem> Data = new ObservableCollection<DataItem>(); 

public ICollectionView DataView 
{ 
    get { return _dataView; } 
    set 
    { 
     if (_dataView != value) 
     { 
      _dataView = value; 
      RaisePropertyChange(() => DataView); 
     } 
    } 
} 

private void LoadData() 
{ 
    SetBusy("Loading..."); 

    Data.Clear(); 

    Task[] tasks = new Task[3] 
    { 
     LoadTools(), 
     LoadResources(), 
     LoadPersonel() 
    }; 

    Task.WaitAll(tasks); 

    DataView = CollectionViewSource.GetDefaultView(Data); 
    DataView.Filter = FilterTimelineData; 

    IsBusy = false; 
} 

private Task LoadResources() 
{ 
    return ResourceService.LoadResources(resources => 
    { 
     foreach(var r in resources) 
     { 
      var d = convertResource(r); 
      Data.Add(d); 
     } 
    }, 
    error => 
    { 
     // do some error handling 
    }); 
} 

यह लगभग काम करता है लेकिन छोटे मुद्दों के एक जोड़े हैं।

संख्या 1: बहुत शुरुआत में SetBusy पर कॉल करने से पहले, मैंने कोई कार्य शुरू करने से पहले और WaitAll पर कॉल करने से पहले, मैंने IsBusy संपत्ति को सत्य पर सेट किया था। यह यूआई को अपडेट करना चाहिए और BusyIndicator नियंत्रण दिखाएं लेकिन यह काम नहीं कर रहा है। मैंने सरल स्ट्रिंग गुण जोड़ने और उनको बाध्य करने का भी प्रयास किया है और उन्हें या तो अपडेट नहीं किया जा रहा है। IsBusy कार्यक्षमता बेस क्लास का हिस्सा है और अन्य दृश्य मॉडल में काम करती है जहां मेरे पास एक से अधिक कार्य चलाना नहीं है, इसलिए मुझे विश्वास नहीं है कि XAML में संपत्ति अधिसूचना या डेटा बाध्यकारी के साथ कोई समस्या है।

पूरी विधि पूरी होने के बाद सभी डेटा बाइंडिंग अपडेट होने लगते हैं। मुझे आउटपुट विंडो में कोई भी "पहली बार अपवाद" या बाध्यकारी त्रुटियां नहीं दिखाई दे रही हैं, जो मुझे विश्वास दिलाती हैं कि यूआई थ्रेड को किसी भी तरह से प्रतीक्षा करने के लिए अवरुद्ध किया जा रहा है।

संख्या 2: मैं सेवा विधियों से गलत कार्य वापस कर रहा हूं। दृश्य मॉडल के बाद कॉलबैक में सभी सेवा विधियों से सभी परिणामों को परिवर्तित करने के बाद मुझे WaitAll के बाद सबकुछ चाहिए। हालांकि अगर मैं सेवा विधि से निरंतरता कार्य वापस करता हूं तो निरंतरता कभी नहीं बुलाती है और WaitAll हमेशा के लिए प्रतीक्षा करता है। अजीब बात यह है कि आईसीओलेक्शन व्यू के लिए यूआई नियंत्रण वास्तव में सब कुछ सही तरीके से प्रदर्शित करता है, मुझे लगता है कि ऐसा इसलिए है क्योंकि डेटा एक अवलोकन संग्रह है और संग्रहव्यूसोर्स संग्रहित घटनाओं के बारे में पता है।

उत्तर

9

आप TaskFactory.ContinueWhenAll का उपयोग कर सकते हैं ताकि एक निरंतरता उत्पन्न हो सके जो इनपुट कार्यों को पूरा करता है।

Task[] tasks = new Task[3] 
{ 
    LoadTools(), 
    LoadResources(), 
    LoadPersonel() 
}; 

Task.Factory.ContinueWhenAll(tasks, t => 
{ 
    DataView = CollectionViewSource.GetDefaultView(Data); 
    DataView.Filter = FilterTimelineData; 

    IsBusy = false; 
}, CancellationToken.None, TaskContinuationOptions.None, 
    TaskScheduler.FromCurrentSynchronizationContext()); 

ध्यान दें कि यह आसान हो जाता है अगर आप का उपयोग सी # 5 की await/async वाक्य रचना:

private async void LoadData() 
{ 
    SetBusy("Loading..."); 

    Data.Clear(); 

    Task[] tasks = new Task[3] 
    { 
     LoadTools(), 
     LoadResources(), 
     LoadPersonel() 
    }; 

    await Task.WhenAll(tasks); 

    DataView = CollectionViewSource.GetDefaultView(Data); 
    DataView.Filter = FilterTimelineData; 

    IsBusy = false; 
} 

लेकिन अगर मैं सेवा विधि से निरंतरता कार्य लौट निरंतरता कहा जाता हो जाता है कभी नहीं और WaitAll हमेशा के लिए इंतजार कर रहा है

समस्या यह है कि आपके निरंतर कार्य को UI थ्रेड की आवश्यकता होती है, और आप WaitAll कॉल में यूआई थ्रेड को अवरुद्ध कर रहे हैं। यह एक डेडलॉक बनाता है जो हल नहीं करेगा।

उपरोक्त को ठीक करने के लिए इसे ठीक करना चाहिए - आप निरंतरता को कार्य के रूप में वापस करना चाहते हैं, क्योंकि आपको पूरा होने की प्रतीक्षा करने की आवश्यकता है - लेकिन TaskFactory.ContinueWhenAll का उपयोग करके आप यूआई थ्रेड को मुक्त कर सकते हैं ताकि यह उन निरंतरताओं को संसाधित कर सके।

ध्यान दें कि यह एक और बात के साथ सी # सरलीकृत हो जाता है कि 5. आप के रूप में अपने अन्य तरीकों में लिख सकते हैं:

internal static async Task LoadResources(Action<IEnumerable<Resource>> completedCallback, Action<Exception> errorCallback) 
{ 
    try 
    { 
    await Task.Run(() => 
    { 
     //... get resources from somewhere 
     return resources; 
    }); 
    } 
    catch (Exception e) 
    { 
    errorCallback(task.Exception); 
    } 

    completedCallback(task.Result); 
} 

कहा जा रहा है, यह तरीकों लिखने के लिए आम तौर पर बेहतर है प्रदान करने के बजाय एक Task<T> वापस जाने के लिए कॉलबैक, क्योंकि यह उपयोग के दोनों सिरों को सरल बनाता है।

+0

मैं एसिंक/प्रतीक्षा का उपयोग कर बंद कर रहा हूं क्योंकि मुझे यकीन नहीं है कि यह WPF कमांड और प्रिज्म प्रतिनिधिमंडल के साथ "बस काम करता है"। कार्य। CONTinueWhenAll मेरे लिए मौजूद प्रतीत नहीं होता है, क्या मुझे कुछ टीपीएल एक्सटेंशन लाइब्रेरी का संदर्भ देने की आवश्यकता है? – BenCr

+0

@BenCr क्षमा करें- यह कार्य FactoryFontinContinueWhenAll। आम तौर पर, प्रतीक्षा/async सामान WPF के साथ कार्य निरंतरता की तुलना में * बेहतर * काम करता है। मुख्य अंतर यह है कि अपवादों को संभालने के लिए आपको पागल हुप्स से कूदना नहीं है। –

+0

धन्यवाद रीड, ऐसा लगता है कि यह बहुत बेहतर काम कर रहा है। उम्मीद है कि कोई भी WaitAll को कॉल करने से पहले अवरुद्ध करने के लिए स्पष्टीकरण प्रदान करने में सक्षम हो सकता है लेकिन इसने निश्चित रूप से 1 और 2 के मुद्दों को ठीक कर दिया है। मैं अगले पुनरावृत्ति में एसिंक/प्रतीक्षा सामग्री दूंगा और देखें कि मैं कैसे चल रहा हूं। – BenCr

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