Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there any other way to set Task.Status to Cancelled

Ok, so I understand how to do Task cancellations using CancellationTokenSource. it appears to me that the Task type "kind of" handles this exception automatically - it sets the Task's Status to Cancelled.

Now you still actually have to handle the OperationCancelledException. Otherwise the exception bubbles up to Application.UnhandledException. The Task itself kind of recognizes it and does some handling internally, but you still need to wrap the calling code in a try block to avoid the unhandled exception. Sometimes, this seems like unnecessary code. If the user presses cancel, then cancel the Task (obviously the task itself needs to handle it too). I don't feel like there needs to be any other code requirement. Simply check the Status property for the completion status of the task.

Is there any specific reason for this from a language design point of view? Is there any other way to set the Status property to cancelled?

like image 877
Simon Avatar asked Dec 17 '14 13:12

Simon


2 Answers

You can set a Task's status to cancelled without a CancellationToken if you create it using TaskCompletionSource

var tcs = new TaskCompletionSource();
var task = tcs.Task;
tcs.SetCancelled();

Other than that you can only cancel a running Task with a CancellationToken

like image 131
i3arnon Avatar answered Oct 17 '22 15:10

i3arnon


You only need to wrap the calling code in a try/catch block where you're asking for the result, or waiting for the task to complete - those are the situations in which the exception is thrown. The code creating the task won't throw that exception, for example.

It's not clear what the alternative would be - for example:

string x = await GetTaskReturningString();

Here we never have a variable referring to the task, so we can't explicitly check the status. We'd have to use:

var task = GetTaskReturningString();
string x = await task;
if (task.Status == TaskStatus.Canceled)
{
    ...
}

... which is not only less convenient, but also moves the handling of the "something happened" code into the middle of the normal success path.

Additionally, by handling cancellation with an exception, if you have several operations, you can put all the handling in one catch block instead of checking each task separately:

try
{
    var x = await GetFirstTask();
    var y = await GetSecondTask(x);
}
catch (OperationCanceledException e)
{
    // We don't care which was canceled
}

The same argument applies for handling cancellation in one place wherever in the stack the first cancellation occurred - if you have a deep stack of async methods, cancellation in the deepest method will result in the top-most task being canceled, just like normal exception propagation.

like image 25
Jon Skeet Avatar answered Oct 17 '22 15:10

Jon Skeet