Reputation: 121
I have the following piece of code:
private void btnAction_Click(object sender, RoutedEventArgs e)
{
/** Clear the results field */
txtResult.Text = "";
/** Disable the button and show waiting status */
btnAction.IsEnabled = false;
lblStatus.Text = "Wait...";
/** Get input from the query field */
string input = query.Text;
/** Run a new task */
Task.Run(() => {
// calling a method that takes a long time (>3s) to finish and return
var attempt = someLibrary.doSomethingWith(input);
// return the result to the GUI thred
this.Dispatcher.Invoke(() =>
{
if (attempt.ContainsKey("success"))
{
if (attempt["success"] == true)
{
txtResult.Text = "Success! The door is: " + (attempt["is_open"] ? "open" : "closed");
lblStatus.Text = "";
}
else
{
lblStatus.Text = "Error! The service says: " + attempt["errorMessage"];
}
}
else
{
MessageBox.Show("There was a problem getting results from web service.");
lblStatus.Text = "";
}
/** Re-enable the button */
btnAction.IsEnabled = true;
});
});
}
Now, I would like to:
Dispatcher.Invoke()
.doSomething()
doSomething()
and after it finished, doAnotherThing()
with the results from the previous callHence why I want to write this using the async model.
What do I do?
Upvotes: 3
Views: 352
Reputation: 81473
You would mark your method as async
, await
the Task.Run
so the continuations run on the UI, also leaving only the long running (seemingly CPU bound) job within it
private async void btnAction_Click(object sender, RoutedEventArgs e)
{
btnAction.IsEnabled = false;
txtResult.Text = "";
lblStatus.Text = "Wait...";
string input = query.Text;
// calling a method that takes a long time (>3s) to finish and return
var attempt = await Task.Run(() => someLibrary.doSomethingWith(input));
if (attempt.ContainsKey("success"))
{
if (attempt["success"] == true)
{
txtResult.Text = "Success! The door is: " + (attempt["is_open"] ? "open" : "closed");
lblStatus.Text = "";
}
else
{
lblStatus.Text = "Error! The service says: " + attempt["errorMessage"];
}
}
else
{
MessageBox.Show("There was a problem getting results from web service.");
lblStatus.Text = "";
}
btnAction.IsEnabled = true;
}
Update
To cancel the task, you would use a CancellationToken
from am instance of CancellationTokenSource
and pass that into Task.Run
and also your long running method to check for IsCancellationRequested
(if you can). You cancel by calling CancellationTokenSource.Cancel
Note you would likely want to wrap this in a try catch finally and catch on OperationCanceledException
and place your button enabling code in the finally
Upvotes: 5
Reputation: 2209
The async
modifier requires that the function return Task<T>
(or void
, in which case any await statements will be ignored). This means that using async
and using Task.Run()
are one and the same, the premise of your question doesn't make sense.
However, what I think you want to do is just use the async await
syntax to avoid an explicit call to Task.Run()
.
Just create a function which returns a Task
async Task<T> Foo()
And then assign a variable var bar=await Foo();
Just use CancellationToken
CancellationTokenSource tokenSource = new CancellationTokenSource();
If you construct a task with two arguments, the second argument is a cancellation token:
var bar= new Task(action, tokenSource.Token)
This lets you use tokenSource.Cancel();
Relevant link: https://learn.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken?view=netframework-4.8
If you don't require a defined order of execution, you can use Task.WhenAll()
, otherwise you can either execute the next task inside the previous one or in the awaiting code.
Upvotes: 2