I have a Task
that I am starting and wish to wait for completion in a WPF app. Inside this task I invoke an Action
on the dispatcher.
If I use Task.Wait()
it appears to hang as if the method never finished. Also, breakpoints inside the Dispatcher.Invoke are never hit.
If I use Task.RunSyncronously()
it appears to work correctly and breakpoints inside the Dispatcher are hit.
Why is there a difference?
Code sample below:
public void ExampleMethod()
{
// When doing the following:
var task = new Task(LoadStuff);
// This never returns:
task.Start();
task.Wait();
// This version, however, does:
task.RunSyncronously();
}
private void LoadStuff()
{
ObservableCollection<StuffObj> stuff = Stuff.Load(arg1, true);
DispatchHelper.RunOnDispatcher(() =>
{
...
});
}
public static class DispatchHelper
{
public static void RunOnDispatcher(Action action)
{
Application.Current.Dispatcher.Invoke(action);
}
}
Yes, there's a major difference. If you use RunSyncronously
you just run the task in the UI thread. If you start it up in a background thread and us Wait
then the code is running in a background thread and the UI thread is blocked. If the code within that task is invoking to the UI thread, and the UI thread is being blocked (by the Wait
) then you've created a deadlock, and the application will remain frozen.
Note that if you used, RunSyncronously
on that task from a non-UI thread, and the UI thread was being blocked by something else, you would still see the deadlock.
Now, as for what you should do, there are really two options here:
The task itself doesn't actually take a long time, and it really should run in the UI thread rather than in a background thread. The UI thread won't be frozen (temporarily) for long enough to be a problem doing all of this work directly in the UI. If this is the case, you probably shouldn't even make it a Task, just put the code in a method and call the method.
The task does take a long time to run, and then it updates the UI after doing that work. If that is the case then it's important that it not be RunSyncronously
but started in a background thread. In order to prevent your entire application from deadlocking it will mean that you'll need to not block the UI thread through a Wait
call. What you need to do if you have some code that you want to run after the task finishes, is to add a continuation to the task. In C# 4.0 this could be done by calling ContinueWith
on the task, and adding in a delegate to be run. In C# 5.0+ you could instead await
on the relevant task (rather than Wait
ing on it, which is actually a big difference) and it will automatically wire up the remainder of the method to run as a continuation for you (in effect it is syntactic sugar for an explicit ContinueWith
call, but it's a very useful one).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With