Reputation: 2234
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
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
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:
StartAsync()
takes the lock on a background thread.PropertyChanged
for MyList
is raised.PropertyChanged
schedules code that accesses MyList
on the UI thread.PropertyChanged
returns.MyList
.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