Reputation: 633
I thought I had a pretty good handle on threading until I came across a scenario in which I wanted to benchmark updates to different grids.
I created a window with a grid control, bound an ObservableCollection
to it and populated it with 5000 rows of some complex data type (which contains no locks).
Then I created a task using
Task.Factory.StartNew()
This went through a very tight loop (10 million iterations) updating a random property on a random item in my ObservableCollection
, each of which raises an INotifyPropertyChanged
event of course.
Now since the updates are all happening on the background thread I expected the UI to update, albeit hard-pressed to keep up with this background thread spinning in a tight loop.
Instead the UI froze for several seconds (but didn't go blank or produce the usual spinning cursor of doom) and then came back once the background thread finished.
My understanding was that the background thread would be taxing a core pretty heavily while producing tons of INPC's, each of which get marshalled automagically by the WPF runtime to the UI thread.
Now the UI thread is doing nothing so I expected it to consume all these INPC's and update the grid but it didn't; not a single update occurred. However, when I do this using a Timer (instead of a tight loop) it works fine.
Would someone please enlighten me as to what the heck the UI thread is doing? Thanks in advance!
Upvotes: 1
Views: 656
Reputation: 27495
If you clog up the message pump with a lot of dispatched updates like this, other messages won't get a chance to be processed, which causes the 'freeze' effect you observe.
One thing that can help here is to use Data Virtualization on your UI control so that only the visible rows are actually bound and listening to INPC updates. This is turned on by default for DataGrid, but if you're using a more custom approach to visualizing the data, this could be an issue.
That said, this won't help with frequent modifications to items that are currently visible, as truly rapid fire updates will still clog up the dispatcher. If you have a use case like this, you probably want to isolate your view model objects a bit and have a way to 'batch' your updates. One technique is to have a way to suppress notification while you do a bunch of updates, then call RaisePropertyChanged(null)
(or whatever equivalent method on your INPC helper base class) on each instance to update all bindings to that instance.
Another mechanism is to make the data updates in some other layer (whatever model object(s) your view model instance is representing), then copy over those properties to the view model class at well-defined intervals. For rapidly updating background data, I often use a polling loop rather than triggering on events, simply because the events would occur more frequently than the UI cares about and it slows down the background processing to send all these unnecessary notifications constantly.
Upvotes: 1