2013-10-01 14 views
7

मुझे एक डब्ल्यूपीएफ यूआई बनाना है, जो वास्तविक समय एफएक्स दर (मुद्रा + दर) अपडेट के लिए सब्सक्राइबर है और उन्हें ग्रिड में प्रदर्शित करता है (लगभग 1000 अपडेट प्रति सेकंड, जिसका मतलब है कि ग्रिड में प्रत्येक पंक्ति मिल सकती है प्रति सेकंड 1000 बार अपडेट किया गया)। ग्रिड में किसी भी समय कम से कम 50 पंक्तियां होंगी।रीयलटाइम में यूआई अपडेट करना

इस के लिए, मैंने एक व्यूमोडेल बनाया है जो अद्यतन घटनाओं की सदस्यता लेता है, और उन अद्यतनों को एक समवर्ती शब्दकोश के अंदर संग्रहीत करता है, जो कि दरव्यूमोडेल ऑब्जेक्ट के रूप में प्रतीक और मूल्य के रूप में महत्वपूर्ण है। तब मेरे पास एक और अवलोकन संग्रह है जिसमें उन सभी रेटव्यूमोडल ऑब्जेक्ट्स हैं, और इसे ग्रिड से बांधें।

कोड:

public class MyViewModel 
    { 
     private readonly IRatesService ratesService; 

     private readonly ConcurrentDictionary<string, RateViewModel> rateDictionary; 
     private object _locker = new object(); 

     public MyViewModel(IRatesService ratesService) 
     { 
      this.ratesService = ratesService; 
      this.ratesService.OnUpdate += OnUpdate; 
      rateDictionary = new ConcurrentDictionary<string, RateViewModel>(); 
      RateViewModels = new ObservableCollection<RateViewModel>();    
     } 

     private void OnUpdate(object sender, RateUpdateEventArgs e) 
     { 
      RateViewModel exisistingRate; 
      if (!rateDictionary.TryGetValue(e.Update.Currency, out exisistingRate)) 
      { 
       exisistingRate = new RateViewModel(new Rate(e.Update.Currency, e.Update.Rate)); 
       rateDictionary.TryAdd(e.Update.Currency, exisistingRate);     
       return; 
      } 

      lock (_locker) 
      { 
       exisistingRate.UpdateRate(e.Update.Rate);     
      } 

      Application.Current.Dispatcher.BeginInvoke(new Action(() => SearchAndUpdate(exisistingRate))); 
     } 

     public ObservableCollection<RateViewModel> RateViewModels { get; set; } 

     private void SearchAndUpdate(RateViewModel rateViewModel) 
     { 
      //Equals is based on Currency 
      if (!RateViewModels.Contains(rateViewModel)) 
      { 
       RateViewModels.Add(rateViewModel); 
       return; 
      } 

      var index = RateViewModels.IndexOf(rateViewModel); 
      RateViewModels[index] = rateViewModel; 
     }  
    } 

मैं इस पर 4 प्रश्न हैं:,

  • वहाँ एक रास्ता मैं ObservableCollection को समाप्त कर सकते है के रूप में यह एक ही आइटम के भंडारण के 2 अलग datastructures के लिए अग्रणी रहा है - लेकिन अभी भी मेरे अपडेट यूआई को रिले किया गया है?

  • मैंने समवर्ती शब्दकोश का उपयोग किया है, जो पूरे अद्यतन ऑपरेशन को लॉक करने की ओर जाता है। क्या पूरे सिद्धांत को लॉक करने या किसी भी डेटास्ट्रक्चर के लिए इसे संभालने का कोई और चालाक तरीका है?

  • मेरा अपडेटरेट विधि भी ताले - मेरे रेटव्यूम मॉडल पर मेरी सभी संपत्तियां केवल कीमत को छोड़कर पढ़ी जाती हैं, क्योंकि यह अपडेट हो रही है। क्या इस परमाणु बनाने का कोई तरीका है, कृपया ध्यान दें कि कीमत एक डबल के रूप में आ रही है।

  • क्या कोई तरीका है कि मैं SearchAndUpdate विधि को अनुकूलित कर सकता हूं, यह 1 से संबंधित है। फिलहाल मुझे विश्वास है कि यह एक ओ (एन) ऑपरेशन है।

.NET 4.0 का उपयोग करना और संक्षिप्तता के लिए INPC नहीं दिखाए हैं।

* संपादित करें: * क्या आप कृपया सभी 4 अंकों को ध्यान में रखते हुए इसे बेहतर तरीके से लिखने में मेरी सहायता कर सकते हैं? Psuedocode करेंगे।

धन्यवाद, -माइक

उत्तर

4

1) मैं लगभग 50 अतिरिक्त refs चिंता नहीं चारों ओर चल

2) हाँ, लॉकसेल डेटा संरचनाओं मुमकिन है। Interlocked क्या आपका मित्र यहां है और वे बहुत सारे एक बंद हैं। ReaderWriterLock एक और अच्छा विकल्प है यदि आप अपने शब्दकोश में अक्सर कौन से आइटम बदल रहे हैं तो नहीं बदल रहे हैं।

3) आम तौर पर, यदि आप यूआई की तुलना में अधिक डेटा से अधिक डेटा से निपट रहे हैं, तो आप पृष्ठभूमि में अपडेट करना चाहते हैं, केवल यूआई थ्रेड पर आईएनपीसी आग लगाना चाहते हैं, और अधिक महत्वपूर्ण रूप से एक सुविधा है यूआई अपडेट ड्रॉप करें (बैकिंग फ़ील्ड को अपडेट करते समय भी)।

  1. अगर यह रिटर्न 1 से बाहर निकलें, क्योंकि वहाँ अभी भी है एक लंबित यूआई अद्यतन
  2. एक निजी क्षेत्र के लिए 1 स्थापित करने के लिए समर्थन क्षेत्र
  3. उपयोग Interlocked.CompareExchange पर एक Interlocked.Exchange करो,: बेसिक दृष्टिकोण की तरह कुछ होने जा रहा है
  4. तो Interlocked.CompareExchange लौटे 0, यूआई के आह्वान और अपनी संपत्ति बदली हुई घटना आग और आप थ्रॉटलिंग क्षेत्र के लिए 0 अद्यतन

4) SearchAndUpdate superf लगता है (तकनीकी रूप से अधिक आप अगर आप गैर x86 के बारे में परवाह करने की जरूरत है) luous ... UpdateRate यूआई को बुलबुला होना चाहिए और आपको केवल यूआई थ्रेड में आमंत्रित करने की आवश्यकता है यदि आपको किसी आइटम को देखने योग्य संग्रह में जोड़ने या निकालने की आवश्यकता है।

अद्यतन: यहां एक नमूना कार्यान्वयन है ... चीजें थोड़ा अधिक जटिल हैं क्योंकि आप युगल का उपयोग कर रहे हैं जो 32 बिट CPUs पर परमाणुता प्राप्त नहीं करते हैं।

class MyViewModel : INotifyPropertyChanged 
{ 
    private System.Windows.Threading.Dispatcher dispatcher; 

    public MyViewModel(System.Windows.Threading.Dispatcher dispatcher) 
    { 
     this.dispatcher = dispatcher; 
    } 


    int myPropertyUpdating; //needs to be marked volatile if you care about non x86 
    double myProperty; 
    double MyPropery 
    { 
     get 
     { 
      // Hack for Missing Interlocked.Read for doubles 
      // if you are compiled for 64 bit you should be able to just do a read 
      var retv = Interlocked.CompareExchange(ref myProperty, myProperty, -myProperty); 
      return retv; 
     } 
     set 
     { 
      if (myProperty != value) 
      { 
       // if you are compiled for 64 bit you can just do an assignment here 
       Interlocked.Exchange(ref myProperty, value); 
       if (Interlocked.Exchange(ref myPropertyUpdating, 1) == 0) 
       { 
        dispatcher.BeginInvoke(() => 
        { 
         try 
         { 
          PropertyChanged(this, new PropertyChangedEventArgs("MyProperty")); 
         } 
         finally 
         { 
          myPropertyUpdating = 0; 
          Thread.MemoryBarrier(); // This will flush the store buffer which is the technically correct thing to do... but I've never had problems with out it 
         } 
        }, null); 
       } 

      } 
     } 
    } 

    public event PropertyChangedEventHandler PropertyChanged = delegate {}; 


}  
+0

क्या आप प्वाइंट 3 के लिए कुछ छद्म कोड पोस्ट कर सकते हैं? – Mike

+0

मैं इस बारे में बहुत स्पष्ट नहीं हूं "इंटरलॉक का उपयोग करें। कॉम्पारे एक्सचेंज एक निजी फ़ील्ड को 1 पर सेट करने के लिए, यदि यह 1 से बाहर निकलता है तो अभी भी एक लंबित यूआई अपडेट है यदि इंटरलॉक.कंपारे एक्सचेंज 0 लौटा, यूआई पर आक्रमण करें और अपनी आग लगाएं संपत्ति ने ईवेंट बदल दिया और आपको थ्रॉटलिंग फ़ील्ड को 0 पर अपडेट किया (तकनीकी रूप से यदि आपको गैर x86 की परवाह है तो आपको और करने की आवश्यकता है) --- --- क्या आप कृपया एक संक्षिप्त कोड स्निपेट पोस्ट कर सकते हैं? – Mike

+0

@ माइक मैंने तकनीक – Yaur

3

माइक -

मैं अलग तरह से इस एक छोटे से दृष्टिकोण होगा। जब तक नई एफएक्स पंक्तियों को जोड़ा नहीं जाता है तब तक आपको वास्तव में एक अवलोकन संग्रह की आवश्यकता नहीं होती है। अवलोकन संग्रह जैसा कि आप जानते हैं केवल आपको उस परिदृश्य में अंतर्निहित परिवर्तन अधिसूचना देता है। यदि आपके पास 50 पंक्तियों (उदाहरण के लिए) की सूची है और Fx ऑब्जेक्ट (जो प्रत्येक व्यक्तिगत पंक्ति का प्रतिनिधित्व करता है) को 1000 बार एक सेकंड अपडेट किया जाता है - तो आप ऑब्जेक्ट पर Fx गुणों पर कनवर्ट किए गए INotifyProperty का बहुत अच्छी तरह से उपयोग कर सकते हैं और उस तंत्र को अपडेट करने दें वे यूआई बदलते हैं। विचार की मेरी रेखा यह है - यह एक संग्रह से दूसरे संग्रह में उन्हें स्थानांतरित करने के बजाय यूआई अपडेट के लिए एक आसान तरीका है

अब आपके दूसरे बिंदु के संबंध में - एक सेकंड में मौजूदा अपडेट (मौजूदा FX ऑब्जेक्ट) - जो तकनीकी रूप से यूआई परिप्रेक्ष्य से अपठनीय है - मैंने जो दृष्टिकोण लिया है वह फ्रीज और थॉ है - जिसका मतलब है कि आप अनिवार्य रूप से इनोटिफाप्रोपर्टी चेंज (यूआई को फायरिंग के रूप में) को रोकते हैं और इसे आवृत्ति के आधार पर रखते हैं - उदाहरण के लिए - प्रत्येक 1 सेकंड - जो भी मेरी स्थिति है सभी एफएक्स ऑब्जेक्ट्स (यूआई रीफ्रेश करें)। अब उस दूसरे के भीतर - एफएक्स गुणों के साथ जो भी अपडेट होते हैं - वे स्वयं पर ओवरराइटिंग रखते हैं - और 1 सेकंड अंतराल होने पर नवीनतम/सही मान - यूआई को दिखाया जाता है। इस तरह - UI पर दिखाए जाने वाले डेटा हमेशा यूआई पर प्रदर्शित होने पर हमेशा सही और प्रासंगिक होते हैं।

+0

एक अद्यतन अंतराल चुनते समय, ध्यान रखें कि उपयोगकर्ता अक्सर अद्यतन किए गए कॉलम द्वारा क्रमबद्ध हो सकता है, इस मामले में न केवल संख्याएं बदल रही हैं, लेकिन पंक्तियां भी स्थिति बदल रही हैं (दृश्यों को दृष्टि से ट्रैक करने के लिए कठिन)। मुझे लगता है कि पैट्रिक स्थगित आईएनपीसी अपडेट के साथ मर चुका है, और आप * संपत्तियों में अधिसूचनाओं को ध्वस्त करके और अधिक अनुकूलित करने में सक्षम हो सकते हैं (संपत्ति नाम के लिए 'नल' के साथ एक एकल संपत्ति बदलें ईवेंट बढ़ाएं)। अधिकांश ग्रिड पूरी पंक्ति को रीफ्रेश करके सही ढंग से संभालते हैं, और कुछ ग्रिड (जैसे DevExpress) वैसे भी प्रत्येक संपत्ति परिवर्तन के लिए ऐसा करेंगे। –

0

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

पहला यह है कि आपको यूआई थ्रेड के अपडेटों को मार्शल करना होगा - मौजूदा व्यू मॉडेल के अपडेट के लिए आपके लिए किया गया है, जो आपके लिए नए/हटाए गए व्यू मॉडेल के लिए नहीं किया गया है। एक 1000 अपडेट एक सेकंड के साथ आप शायद यूआई थ्रेड के लिए मार्शलिंग की ग्रैन्युलरिटी को नियंत्रित करना चाहते हैं और इसके संदर्भ में संदर्भ स्विचिंग को नियंत्रित करना चाहते हैं। इयान ग्रिफिथ्स ने इस पर एक महान blog series लिखा था।

दूसरी बात यह है कि यदि आप चाहते हैं कि आपका यूआई उत्तरदायी रहे, तो संभवतः आप जितना संभव हो उतना जीन 2 कचरा संग्रह से बचना चाहते हैं जिसका अर्थ है जीसी पर दबाव कम करना। यह आपके मामले में एक समस्या हो सकती है क्योंकि आप प्रत्येक अद्यतन के लिए एक नया दर ऑब्जेक्ट अपडेट बनाते हैं।

एक बार जब आप कुछ स्क्रीनें शुरू कर देते हैं जो एक ही चीज करते हैं तो आप इस अद्यतन व्यवहार को एक आम घटक में अमूर्त करने के लिए एक रास्ता खोजना चाहते हैं। अन्यथा आप अपने व्यूमोडल्स के माध्यम से थ्रेडिंग कोड छिड़केंगे जो त्रुटि प्रवण है।

मैंने एक ओपन सोर्स प्रोजेक्ट, ReactiveTables बनाया है, जो इन तीन चिंताओं को संबोधित करता है और कुछ अन्य विशेषताओं को जोड़ता है जैसे कि फ़िल्टर करने, क्रमबद्ध करने, अपने मॉडल संग्रह में शामिल होने में सक्षम होना। इसके अलावा डेमो दिखाते हैं कि सर्वश्रेष्ठ प्रदर्शन प्राप्त करने के लिए वर्चुअल ग्रिड के साथ इसका उपयोग कैसे किया जाए। शायद यह आपको बाहर निकालने/प्रेरित करने में आपकी मदद कर सकता है।

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