Most programming language allow you to take advantage of multi-threaded execution, and .NET is no exception!
Asynchronous code is a modern abstraction of multi-threading execution, and as you hopefully are aware, in .NET land, this is achieved with the async
and await
keywords.
For the Universal Windows Platform, we can also count on the CoreDispatcher class to marshal execution back to the main thread, from which we can update the UI of our apps.
Here’s a simple example of how to use it:
public async void DoStuff()
{
var dispatcher = CoreApplication.MainView.CoreWindow.Dispatcher;
await dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
UpdateUI();
});
DoOtherStuff();
}
Assuming we are on a background thread when the DoStuff()
method is invoked, we will retrieve a CoreDispatcher
instance from the CoreWindow.CoreDispatcher
property, call and await for the execution of dispatcher.RunAsync()
method, which in turn will invoke the UpdateUI()
method on the main thread, and then code execution will continue in the background thread by invoking the DoOtherStuff()
method.
As it is right now, we know that DoOtherStuff
will only execute after the UpdateUI()
method finishes, but now let’s assume that we replace the UpdateUI()
synchronous method with an asynchronous version of it, called UpdateUIAsync()
:
public async void DoStuff()
{
var dispatcher = CoreApplication.MainView.CoreWindow.Dispatcher;
await dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
await UpdateUIAsync();
});
DoOtherStuff();
}
In this new version of the code, you’ll notice that the DoOtherStuff()
method will eventually run before the UpdateUIAsync()
has finished, which might not be what you intended to in the first place when you await’ed for the dispatcher.RunAsync()
method!
This is due to the fact that the CoreDispatcher.RunAsync method has a DispatchedHandler callback which is not asynchronous:
public delegate void DispatchedHandler()
The fact that we’ve added the async/await
keywords to the callback doesn’t ensure that the caller will await for it to execute!
There are a few ways suspend the background thread execution until the foreground thread signals for it to continue, and using a TaskCompletionSource<T> is one of the easiest:
public async void DoStuff()
{
var dispatcher = CoreApplication.MainView.CoreWindow.Dispatcher;
var taskCompletionSource = new TaskCompletionSource<bool>();
await dispatcher.RunAsync(CoreDispatcherPriority.Normal, async () =>
{
await UpdateUIAsync();
taskCompletionSource.SetResult(true);
});
await taskCompletionSource.Task;
DoOtherStuff();
}
In this version of the code, once the execution returns from await’ing the dispatcher.RunAsync()
call, the background thread will carry on execution, but will then await for the taskCompletionSource.Task
to finish, which will only happen after the taskCompletionSource.SetResult(true)
call that we make in the main thread!
I’ve written a few CoreDispatcher extension methods to help around this issue, and added them to the next version of the Cimbalino Toolkit, but you can access them right now if you wish so! ;)