When a method that gets called on a worker thread needs to run code on the UI thread and wait for it to complete before doing something else, it can be done like this:
public int RunOnUi(Func<int> f)
{
int res = Application.Current.Dispatcher.Invoke(f);
return res;
}
But what if I wanted to do it with tasks? Is there a way for the RunOnUi method to create a task that is started on the UI and return it so that the caller (which runs on a worker thread) can wait for it? Something that will fit the following signature: public Task<int> StartOnUi(Func<int> f)
?
One way to do it is as follows:
public Task<int> RunOnUi(Func<int> f)
{
var task = new Task<int>(f);
task.Start(_scheduler);
return task;
}
Here, assume that _schduler
holds the ui TaskScheduler
. But I am not too comfortable with creating "cold" tasks and using the start method to run them. Is that the "recommended" way or is there a more elegant way to do it?
Just use InvokeAsync
instead of Invoke
then return the Task<int>
inside the DispatcherOperation<int>
the function returns.
//Coding conventions say async functions should end with the word Async.
public Task<int> RunOnUiAsync(Func<int> f)
{
var dispatcherOperation = Application.Current.Dispatcher.InvokeAsync(f);
return dispatcherOperation.Task;
}
If you do not have access to .NET 4.5 it is a little more complicated. You will need to use BeginInvoke
and a TaskCompletionSource
to wrap the DispaterOperation
that BeginInvoke
returns
public Task<int> RunOnUi(Func<int> f)
{
var operation = Application.Current.Dispatcher.BeginInvoke(f);
var tcs = new TaskCompletionSource<int>();
operation.Aborted += (sender, args) => tcs.TrySetException(new SomeExecptionHere());
operation.Completed += (sender, args) => tcs.TrySetResult((int)operation.Result);
//The operation may have already finished and this check accounts for
//the race condition where neither of the events will ever be called
//because the events where raised before you subscribed.
var status = operation.Status;
if (status == DispatcherOperationStatus.Completed)
{
tcs.TrySetResult((int)operation.Result);
}
else if (status == DispatcherOperationStatus.Aborted)
{
tcs.TrySetException(new SomeExecptionHere());
}
return tcs.Task;
}
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