Home » c# » c# – Using async instead of Task.Run()-Exceptionshub

c# – Using async instead of Task.Run()-Exceptionshub

Posted by: admin February 24, 2020 Leave a comment

Questions:

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:

  • Write the same code in a way that uses a callback instead of using Dispatcher.Invoke().
  • Be able to cancel a running task that calls doSomething()
  • Be able to chain multiple calls, i.e. await doSomething() and after it finished, doAnotherThing() with the results from the previous call

Hence why I want to write this using the async model.

What do I do?

How to&Answers:

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

Answer:

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().

Callbacks

Just create a function which returns a Task

async Task<T> Foo()

And then assign a variable var bar=await Foo();

Cancel a running task

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://docs.microsoft.com/en-us/dotnet/api/system.threading.cancellationtoken?view=netframework-4.8

Chaining Calls

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.

Task.WhenAll(): https://docs.microsoft.com/en-us/dotnet/api/system.threading.tasks.task.whenall?view=netframework-4.8#System_Threading_Tasks_Task_WhenAll_System_Collections_Generic_IEnumerable_System_Threading_Tasks_Task__