Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Updating UI in realtime

Tags:

c#

wpf

I have to create a WPF UI, which subsribes to real time Fx Rate(Currency + rate) updates and displays them in a grid (roughly 1000 updates per second, which means each row in the grid could get updated upto 1000 times per second).The grid would have atleast 50 rows at any point in time.

Towards this, I have created a Viewmodel which subscribes to the update events, and store those updates inside a concurrent dictionary with key as symbol and value as a RateViewModel object. Then I have another observable collection which has all those rateviewmodel objects, and bind that to a grid.

Code:

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;
        }      
    }

I have 4 questions over this:

  • Is there a way I can eliminate the ObservableCollection, as it's leading to 2 different datastructures storing the same items - but still have my updates relayed to the UI?

  • I have used Concurrent Dictionary, which leads to locking the whole update operation. Is there any other clever way of handling this rather than locking the whole dicitionary or for that matter any datastructure?

  • My UpdateRate method also locks - all my properties on my RateviewModel is read only except the price, as this is getting updated. Is there a way to make this atomic, please note that the price is coming in as a double.

  • Is there a way I can optimize the SearchAndUpdate method, this is kind of related to 1st. At the moment I believe it's an O(n)operation.

Using .NET 4.0 and have omitted INPC for brevity.

*EDIT:*Could you please help me in rewriting this in a better manner taking all the 4 points into account? Psuedocode will do.

Thanks, -Mike

like image 420
Mike Avatar asked Oct 01 '13 23:10

Mike


2 Answers

1) I wouldn't worry about 50 extra refs floating around

2) Yes, lockless data structures are doable. Interlocked Is your friend here and they are pretty much all one offs. ReaderWriterLock is another good option if you aren't changing what items are in your dictionary often.

3) Generally, if you are dealing with more data more data than the UI can handle you are going to want to do the updates in the background, only fire INPC on the UI thread, and more importantly have a facility to drop UI updates (while still updating the backing field). Basic approach is going to be something like:

  1. Do an Interlocked.Exchange on the backing field
  2. Use Interlocked.CompareExchange to set a private field to 1, if this returns 1 exit becuase there is still a pending UI update
  3. If Interlocked.CompareExchange returned 0, invoke to the UI and fire your property changed event and update you throttling field to 0 (technically there is more you need to do if you care about non x86)

4) SearchAndUpdate Seems superfluous... UpdateRate should be bubbling to the UI and you only need to Invoke to the UI thread if you need to add or remove an item to the observable collection.

Update: here is a sample implementation... things are little more complicated because you are using doubles which don't get atomicity for free on 32 bit 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 {};


}    
like image 99
Yaur Avatar answered Nov 13 '22 15:11

Yaur


Mike -

I would approach this a little differently. You really dont need an Observable Collection unless new Fx rows are being added. Observable Collection as you know only gives you built-in change notification in that scenario. If you have a list of 50 rows (for example) and the Fx object (which represents each individual row) is updated 1000 times a second - then you can very well use the INotifyPropertyChanged on the Fx Properties on the Object and let that mechanism update the UI as they change. My line of thought is - this is a simpler approach for UI updates rather than move them from one collection to another

Now with regards to your second point - 1000 updates in a second (to an existing FX object) - which technically is unreadable from a UI perspective - the approach I have taken is freeze and thaw - which means you essentially intercept the InotifyPropertyChanged (as its firing to the UI) and keep it frequency based - so for example - every 1 sec - whatever my status of all FX objects is (refresh the UI). Now within that second - whatever updates happen to the FX properties - they keep overwriting on themselves - and the latest/correct value when the 1 second interval happens - is shown to UI. That way - data being shown to UI is always correct and relevant when its displayed to UI.

like image 43
Patrick Avatar answered Nov 13 '22 14:11

Patrick