Dispatch Thread being Saturated - Looking for Design Pattern Change from vanilla MVVM
Perhaps 'saturated' is the wrong word but essentially my Dispatch thread is constantly busy due to she sheer number of UI updates I have.
Until this point I have followed (and enjoyed) what I would call the vanilla MVVM design pattern. The key resources I've used to guide my development are listed in the excellent accepted answer to MVVM: Tutorial from start to finish.
Specifically;
- Jason Dolinger's presentation on the Model-View-ViewModel
Rough Architecture
I have the following namespaces;
- Data
- Connects to an external service and listens for updates
- Receives a packet of updates every 1-2 s
- Single 'Dispatching Source' which schedules all ViewModel updates on the Dispatcher for the application
- ViewModel
- All classes created on the UI thread and all Methods are called on the UI thread
- Handles all translation from Data to GUI code
- View
- Xaml classes
- Very little (If any) Code Behind or transformation logic
- Only heavyweight component is the XCeed DataGrid which sorts data internally
Data Set
At its peak the Dispatching source is scheduling 65,000 events on the dispatch thread 开发者_如何学Pythonper minute.
1,000 events per second where each event will cause a change to approximately 4 bindings. There's very little logic (But there is some) used to determine the new values, typically there may be a switch on a string or a hash lookup to find a Color for the background of a row.
Problem
I would imagine it's obvious by now but responsiveness is very poor. Sometimes the UI will lock up altogether.
If I half the load then the GUI is fine so it's not an order of magnitude off but It would be nice if it could cope with an order of magnitude more.
Is there a good Design Pattern for such a situation?
I suggest that this problem is not unique to me and am hoping that there are people much smarter than myself who have tried to devise a solution. It seems obvious that I need to
Many and Lightweight VS Infrequent and Heavyweight
Rather than 1000s of individual updates (method calls) dispatched on the UI thread, each changing 4 bindings, I could somehow do the changes in a background thread and then update 4000 bindings in a batch method which runs on the UI thread.
This would reduce the overhead spent on dispatching a method call but presumably would lock the UI any time there one of these batches executed. Not ideal.
Bottom Line
I'm at a loss for what to do, I feel like I need to make all changes to objects on a background thread and then poll the state at an interval (Every two seconds?) so that the UI remains responsive.
This doesn't sound like the MVVM that I know and love though. Is there an appropriate way to bind to a dataset which changes at this speed?
You could create your base/root ViewModel that everything binds to and have it implement INotifyPropertyChanged
- but only fire PropertyChanged
events using a timer, if any properties have changed.
When a property changes -> add it to a unique 'dirty' list. Timer elapses -> fire all events in that list, reset list.
Kieren Johnstone is on the right track. I think the answer might be even simpler, not requiring a timer at all.
Implement the members below in your view model base class. Have your data service set SuppressPropertyChanged
on the view model objects at the start of its updates, and clear it when it's done.
protected void OnPropertyChanged(string propertyName)
{
if (SuppressPropertyChanged)
{
return;
}
PropertyChangedEventHandler h = PropertyChanged;
if (h != null)
{
h(this, new PropertyChangedEventArgs(propertyName);
}
}
private bool _SuppressPropertyChanged;
public bool SuppressPropertyChanged
{
get { return _SuppressPropertyChanged; }
set
{
if (_SuppressPropertyChanged != value)
{
_SuppressPropertyChanged = value;
if (!_SuppressPropertyChanged)
{
PropertyChangedEventHandler h = PropertyChanged;
if (h != null)
{
// using null tells the listener to refresh all properties
h(this, new PropertyChangedEventArgs(null);
}
}
}
}
It's hard to say without knowing more about your application. If doing this will fire off thousands of bindings, you might want to maintain a HashSet<string>
that contains the names of the properties that changed while property-change notification was suppressed, and iterate over the set when suppression is turned off.
精彩评论