3

I'm wondering what I need to do to make models thread safe in MVVM. Say I had the following class, which is instantiated as a singleton:

public class RunningTotal: INotifyPropertyChange
{
   private int _total;
   public int Total
   {
      get { return _total; }
      set
      {
         _total = value;
         PropertyChanged("Total");
      }
   }
   ...etc...
}

My view model exposes it via a property:

public RunningTotal RunningTotal { get; }

And my view has a textblock bound to it, i.e. {Binding Path=RunningTotal.Total}.

My app has a background thread that periodically updates the value of Total. Assuming nothing else updates Total, what (if anything) should I do to make all this thread-safe?

Now, what if I wanted to do something similar but using a property of type Dictionary<>, or ObservableCollection<>? Which members (add, remove, clear, indexer) are thread-safe? Should I use a ConcurrentDictionary instead?

Andrew Stephens
  • 9,413
  • 6
  • 76
  • 152
  • See also: [volatile](http://msdn.microsoft.com/en-us/library/x13ttww7(v=vs.71).aspx), [Is accessing a variable in C# an atomic operation?](http://stackoverflow.com/questions/9666/is-accessing-a-variable-in-c-sharp-an-atomic-operation) – Sjoerd Jun 13 '12 at 13:23
  • Try the following link which provides a thread-safe ObservableCollection type solution that works from any thread and can be bound to via multiple UI threads : http://www.codeproject.com/Articles/64936/Multithreaded-ObservableImmutableCollection – Anthony Apr 15 '14 at 19:21

6 Answers6

8

My app has a background thread that periodically updates the value of Total. Assuming nothing else updates Total, what (if anything) should I do to make all this thread-safe?

For scalar properties, you don't need to do anything special; the PropertyChanged event is automatically marshaled to the UI thread.

Now, what if I wanted to do something similar but using a property of type Dictionary<>, or ObservableCollection<>? Which members (add, remove, clear, indexer) are thread-safe? Should I use a ConcurrentDictionary instead?

No, this is not thread-safe. If you change the content of an ObservableCollection<T> from a background thread, it will break. You need to do it on the UI thread. An easy way to do it is to use a collection that raises its events on the UI thread, like the one described here.

As for Dictionary<TKey, TValue>, it doesn't raise a notification when its content changes, so the UI is not notified anyway.

Thomas Levesque
  • 286,951
  • 70
  • 623
  • 758
  • 2
    How does it happen that `PropertyChanged` is automatically marshaled to the UI thread? Where is the magic? I would expect the event to happen in the context where it's fired. – Vlad Jun 13 '12 at 13:25
  • @Vlad, the "magic" is in the binding engine. The `PropertyChanged` event itself might be fired on a background thread, but the binding engine reacts to it on the UI thread (using Dispatcher.Invoke) – Thomas Levesque Jun 13 '12 at 13:28
  • Well, but what happens if the View code subscribes to the event manually with `+=` and then tries to update the UI inside the event handler? Isn't this supposed to crash? – Vlad Jun 13 '12 at 13:30
  • 1
    @ThomasLevesque This is news to me, can you provide some documentation of that? – CodingGorilla Jun 13 '12 at 13:31
  • The comments in [this article](http://msdn.microsoft.com/en-us/library/bb613588(v=vs.110).aspx) seem to indicate that accessing collections from multiple threads is safe in WPF version 4.5. –  Jun 13 '12 at 13:35
  • @Vlad this is an interesting question - gut feeling would have been to agree with you. However my app as it stands works, and the property is actually a complex type (I used an int to keep my question simple), so even more confused now, especially after Thomas saying you don't need to do anything special for scalars! – Andrew Stephens Jun 13 '12 at 13:37
  • 3
    @Coding Gorilla: See this [MS Connect article](http://connect.microsoft.com/VisualStudio/feedback/details/624517/binding-should-check-for-cross-thread-calls). It confirms Thomas' claim that bindings are automatically marshalled. –  Jun 13 '12 at 13:38
  • Learn something new every day! Thanks for the link; here and I was doing all this work myself. =) – CodingGorilla Jun 13 '12 at 13:41
  • @fmunkert: this however leaves the problem open for the case when the View just subscribes to the event manually, without `Binding`. So as far as I understand the VMs still have no freedom to fire the event in whichever thread they like to. – Vlad Jun 13 '12 at 13:41
  • @Vlad, in that case, yes, it will crash (unless you explicitly perform the action on the UI thread with Dispatcher.Invoke). – Thomas Levesque Jun 13 '12 at 13:47
  • @CodingGorilla, sorry, I don't know about any documentation that mentions this explicitly. It's just what I have observed. – Thomas Levesque Jun 13 '12 at 13:47
  • @ThomasLevesque Just to confirm, using OPs scenario, theres no problems doing this with a scalar (i.e. primitive numeric types). If OPs property was a ```string``` instead, is it safe to say we would have a concurrency issue as the worker thread could update the string as the UI thread is reading it to display? – bgura Feb 27 '20 at 16:54
  • 1
    @bgura no, it would work fine with a string. String is an immutable reference type, so changing the string just replaces the reference to the original string with a reference to the new one, which is done atomically. – Thomas Levesque Mar 20 '20 at 10:43
2

The model should be written in a thread-safe way just like any code must be; it's up to you to determine whether you are using locks, concurrent containers or anything other to do it. The models are just library code, which (almost) shouldn't know that its functionality is going to be consumed by a MVVM application.

The VMs, however, have to work in the UI thread. This means that typically they cannot rely that the events from model are coming in the UI thread, so they have to marshal the calls or store them in a task queue if the subscribed events are coming not at the UI thread.

So, the VM is the place which should care about thread safety in a specific way, more than the model needs to.

The View code, in turn, can usually live in happy ignorance about all the threading issues: it gets all the messages/calls/events/whatever in the dedicated UI thread, and makes it own calls in the UI thread as well.


Specifically for your case, your code is not the model but VM, right? In this case you have to fire your events in the UI thread, otherwise the View will be unhappy.

Vlad
  • 35,022
  • 6
  • 77
  • 199
2

Say that there are two threads updating Total, and you want to log all changes to _total in the PropertyChanged method. Now there is a race condition in which PropertyChanged can miss a value. This happens when a thread blocks in the middle of calling set_Total. It updates _total but does yet call PropertyChanged. In the meantime, another thread updates _total to another value:

thread1: _total = 4;
thread2: _total = 5;
thread2: PropertyChanged("Total");
thread1: PropertyChanged("Total");

Now, PropertyChanged is never called with the value of 4.

You can solve this by passing the value to the PropertyChanged method, or by using a lock in the setter.

Since you say that you have one thread which updates this property, there is no possibility for a race condition. This is only the case when multiple threads (or processes) update the same thing at the same time.

Sjoerd
  • 74,049
  • 16
  • 131
  • 175
1

This question provides a thread-marshalled version of the ObservableCollection

How do you correctly update a databound datagridview from a background thread

However, you still need to worry about contention between threads, which would require you to lock resources when they are updated, or use something like Interlocked.Increment.

If one thread is updating, and another is reading, there exists the possiblity that a read is done half way through an update (For example, an Int64 is being modified. The first half (32 bits) has been updated in one thread, and before the second half has been updated, the value is read from a second thread. A completely wrong value is read)

This may or may not be a problem depending on what your application is going to do as a result. If the wrong value will be flashed on the GUI for 1 second, then its probably not a big deal, and the performance penalty of the lock can be ignored. If your program is going to take action based on that value, then you probably need to lock it down.

Community
  • 1
  • 1
Jason Coyne
  • 6,509
  • 8
  • 40
  • 70
0

A simple answer is that you need to schedule the property updates in UI Thread through UI Thread's Dispatcher. This will put updates operation in a queue which will not crash the application.

private void handler(object sender, EventArgs e)
{
    Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Normal, (ThreadStart)delegate { updates(); });
}

private void updates() { /* real updates go here */ }
Yang Zhang
  • 4,540
  • 4
  • 37
  • 34
0

Some thing more elaborate that it is actually so simple... When instantiating your viewmodel in your view just pass the dispatcher down in the ctor.

    ServerOperationViewmodel ViewModel;        
    public pgeServerOperations()
    {
        InitializeComponent();
        ViewModel = new ServerOperationViewmodel(Dispatcher);
    }

Then in your View model:

Dispatcher UIDispatcher;
public ServerOperationViewmodel(Dispatcher uiDisp)
{
    UIDispatcher = uiDisp;
}

And use it like a normal UI dispatcher.

UIDispatcher.Invoke(() =>
{
  .......
});

I will admit that I am still fairly new to MVVM, but I don not think this breaks the MVVM motto.

DR.
  • 573
  • 6
  • 20