Heinrich
Heinrich

Reputation: 2234

Why Doesn't the Code Deadlock

Can some please explain to me why this code doesn't cause a Deadlock?

static Object listlock = new Object();

void StartAsync()
{
    System.Threading.Tasks.Task.Factory.StartNew(() => 
        {
            lock(listlock)
                base.OnPropertyChanged("MyList");
        });
}

 public ObservableCollection<MyObjects> MyList
 {
    get
    {
         lock(listlock)
             return  new ObservableCollection<MyObjects>(_myObjectList);
    }
 }

Some background details:
The program is using MVVM Pattern and MyList is bound to a Datagrid on the WPF UI.
_myObjects is just a random list of Objects.

Is it because the OnPropertyChange just informs the UI that it has to go get the New Data from MyList and just returns without caring whether the UI actually gets the Data or Not?
I know that the OnPropertyChanged is called on a separate Thread but the UI exists on a single thread (doesn't it O.o) so the thread that gets informed is also the thread that gets the Data. I would of thought that lock wouldn't have been able to release because of that?

Upvotes: 1

Views: 295

Answers (2)

Alexei Levenkov
Alexei Levenkov

Reputation: 100620

For deadlock to happen on single lock you need 2 threads in such a way that first thread grabs the lock and wait for second thread to grab the same lock. You will not get deadlock otherwise (i.e. there is no wait for other thread inside lock statement, or there is only one thread involved).

Note to future readers - below is not what happens in WPF case - see svick's answer. Just generic example of no deadlock with 2 locks on the same thread:

One possible case is when listener for OnPropertyChanged calls MyList in response to the synchronous notification on the same thread (check out call stack when MyList is called). In this case nested lock does nothing since thread requesting the lock already holds it.

Upvotes: 3

svick
svick

Reputation: 245038

The key realization here is that the handler of PropertyChanged does schedule some code that accesses MyList on the UI thread, but it does not wait for it to finish.

So, one possible sequence of events is this:

  1. StartAsync() takes the lock on a background thread.
  2. PropertyChanged for MyList is raised.
  3. The handler for PropertyChanged schedules code that accesses MyList on the UI thread.
  4. The handler for PropertyChanged returns.
  5. The lock is released.
  6. UI thread attempts to access MyList.
  7. UI thread takes the lock without waiting, because no other thread owns it.

Another way to put it: What I think you expected is that the handler for PropertyChanged does something like:

Dispatcher.Invoke(() =>
{
    if (e.PropertyName == "MyList")
    {
        var newList = model.MyList;
        // set newList as the current value of some binding
    }
});

But in fact, it does something like (the only difference is the first line):

Dispatcher.BeginInvoke(() =>
{
    if (e.PropertyName == "MyList")
    {
        var newList = model.MyList;
        // set newList as the current value of some binding
    }
});

Upvotes: 3

Related Questions