I have a WPF program with a button which creates and displays some data which is databound to a grid. The process of creating the data is very slow and CPU bound hence I offload it to a task. I want to display the first chunk of data as soon as its ready, then display the second chunk.
Here are 3 implementations which all work and keep the UI responsive.
await Dispatcher.InvokeAsync, Dispatcher.Invoke and Dispatcher.Invoke (inside the Task.Run). Which of these is going to avoid blocking a thread on the threadpool that could otherwise be doing work, and which is the least likely to result in a deadlock if someone had blocked the UI thread elsewhere in the program?
public ObservableCollection<BigObject> DataBoundList {get;set;}
public ObservableCollection<BigObject> DataBoundList2 {get;set;}
//Click handler from WPF UI button
public async void ClickHandlerCommand()
{
List<BigObject> items1 = null;
List<BigObject> items2 = null;
//On UI Thread
await Task.Run(() =>
{
//On thread X from threadpool
items1 = SlowCPUBoundMethod1();
}).ConfigureAwait(false);
Dispatcher.Invoke(() =>
{
//On UI Thread
DataBoundList = new ObservableCollection<BigObject>(items1);
RaisePropertyChanged(nameof(DataBoundList));
});
//On thread X from threadpool
await Task.Run(() =>
{
//On thread Y from threadpool
items2 = SlowCPUBoundMethod2();
}).ConfigureAwait(false);
//On thread Y from threadpool
Dispatcher.Invoke(() =>
{
//On UI Thread
DataBoundList2 = new ObservableCollection<BigObject>(items2);
RaisePropertyChanged(nameof(DataBoundList2));
});
//On thread Y from threadpool
//5x context switches
}
The implementation above puts the dispatcher call outside the Task.Run. This will likely cause two threads to be spun up. If another thread someone in the program had blocked the UI thread then I think the Dispatcher.Invoke call would possibly deadlock?
public async void ClickHandlerCommand2()
{
List<BigObject> items = null;
List<BigObject> items2 = null;
//On UI Thread
await Task.Run(() =>
{
//On thread X from threadpool
items1 = SlowCPUBoundMethod1();
Dispatcher.Invoke(() =>
{
//On UI thread
DataBoundList = new ObservableCollection<BigObject>(items1);
RaisePropertyChanged(nameof(DataBoundList));
});
//On thread X from threadpool
items2 = SlowCPUBoundMethod2();
Dispatcher.Invoke(() =>
{
//On UI thread
DataBoundList2 = new ObservableCollection<BigObject>(items2);
RaisePropertyChanged(nameof(DataBoundList2));
});
//On thread X from threadpool
}).ConfigureAwait(false);
//On thread X from threadpool
//5x context switches
}
The implementation above will have a single thread, however if another thread someone in the program had blocked the UI thread then I think the Dispatcher.Invoke call would possibly deadlock?
public async void ClickHandlerCommand3()
{
List<BigObject> items1 = null;
List<BigObject> items2 = null;
//On UI Thread
await Task.Run(() =>
{
//On thread X from threadpool
items1 = SlowCPUBoundMethod1();
}).ConfigureAwait(false);
//On thread X from threadpool
await Dispatcher.InvokeAsync(() =>
{
//On UI Thread
DataBoundList = new ObservableCollection<BigObject>(items1);
RaisePropertyChanged(nameof(DataBoundList));
});
//On thread X from threadpool
items2 = SlowCPUBoundMethod2();
await Dispatcher.InvokeAsync(() =>
{
//On UI Thread
DataBoundList2 = new ObservableCollection<BigObject>(items2);
RaisePropertyChanged(nameof(DataBoundList2));
});
//On thread X from threadpool
//5x context switches
}
This should result in only 1 task being spun up and I believe reduce the risk of a deadlock if someone somewhere else has blocked the UI thread. I think this is the best implementation?
Can someone categorically say which is the correct implementation? I believe the third example using await Dispatcher.InvokeAsync is the correct one but I'm not completely sure.