5

When I cancel a Task, the await result still returns true for the IsCanceled Property. Seems something is going wrong.

Please advise. This is the code:

CancellationTokenSource _cancelLationToken = new CancellationTokenSource();

private async void Button_Click(object sender, EventArgs e)
{
    _cancelLationToken = new CancellationTokenSource();
    _cancelLationToken.Token.Register(theCallBack);
    var myTaskToWaitFor = Task.Factory.StartNew(() => WorkHard(_cancelLationToken.Token), _cancelLationToken.Token);
    await myTaskToWaitFor;

    int i=0;

    if(myTaskToWaitFor.IsCanceled)
        i = i; //breakpoint for debugging
    else
        i = i; //breakpoint for debugging <== always ends here... :-(
}

private void WorkHard(CancellationToken token)
{
    for(int i = 0; i < 100000000; i++)
        if(token.IsCancellationRequested)
            break;
        else
            Math.Acos(Math.Pow(i, i / 10000000));
}

public void theCallBack()
{
    //todo: do something
}

private void CancelButton_Click(object sender, EventArgs e)
{
    _cancelLationToken.Cancel();
}

When I hit the CancelButton and therefore trigger the CancelButton_Click method, the CallBack method is triggered. But... when I check myTaskToWaitFor.IsCanceled is always returns false and I get this info: Id = 1, Status = RanToCompletion, Method = "{null}". Why is the status not Cancelled or something?

When I use http://msdn.microsoft.com/en-us/library/system.threading.cancellationtoken.throwifcancellationrequested.aspx

like this:

private void WorkHard(CancellationToken token)
{
    for(int i = 0; i < 100000000; i++)
        if(token.IsCancellationRequested)
            token.ThrowIfCancellationRequested();
        else
            Math.Acos(Math.Pow(i, i / 10000000));
}

I only get some errors in debug mode and the it seems the myTaskToWaitFor is actually cancelled, but the code doesn't continue (on continue the program is shut down due to the exception). Any thoughts? Kind regards,

Matthijs

Edit:

test with the TrowIfCancellationRequested was based on "When you call ThrowIfCancellationRequested on a CancellationToken which is canceled, it will throw an OperationCanceledException. The Task Parallel Library knows that this type of exception represents cancellation rather than a failure, and will treat it differently. For example, Task has a property called IsCanceled that automatically becomes true when an OperationCanceledException is thrown while executing an async method." From the book Async in C# 5.0 by Alex Davies.

That's why I thought I could expect myTaskToWaitFor.IsCancelled to return true and the code would continue.

user369122
  • 792
  • 3
  • 13
  • 33
  • The version with `ThrowIfCancellationRequested` is correct - is something there behaving unexpectedly? It is expected that this will report an exception, though - you need to handle that – Marc Gravell Sep 11 '13 at 10:08
  • How can I handle the exception before the check for myTaskToWaitForGetCashReport.IsCanceled? If I add a try catch around " var myTaskToWaitFor = Task.Factory.StartNew(() => WorkHard(_cancelLationToken.Token), _cancelLationToken.Token); await myTaskToWaitFor;" the field myTaskToWaitFor is unknown (of course...). When I also add the check myTaskToWaitFor.IsCanceled within the try...I only go to the catch part. Do I need to make a bool field before the try and set it either within the try or in the catch? Is that the only solution? (myTaskToWaitFor.IsCanceled is (of course) unknown in the catch) – user369122 Sep 11 '13 at 10:16
  • 1
    you `try` / `catch` around the `await`. The variable is known by then. – Marc Gravell Sep 11 '13 at 10:25
  • ah...sound like a good idea ;-) And that was the solution. Thanks – user369122 Sep 11 '13 at 10:30
  • You should prefer `Task.Run` over `StartNew` in `async` code, [as I describe on my blog](http://blog.stephencleary.com/2013/08/startnew-is-dangerous.html). – Stephen Cleary Sep 11 '13 at 12:49

1 Answers1

7

The ThrowIfCancellationRequested() is the ideal way of signalling that you are existing due to observing cancellation. This will present itself as an exception at the caller, but you can handle that by having a try / catch around the await, and simply checking the reasons there - either on the task, or by inspecting the AggregateException.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • Still a small question. When I don't use this solution in a for loop or something like that, but setup a test within the code like this: ct.ThrowIfCancellationRequested(); No exception seems to be thrown... is this correct? – user369122 Sep 11 '13 at 10:59
  • @user369122 if you aren't in a loop, it is possible that it made it past the check before you cancelled...? – Marc Gravell Sep 11 '13 at 11:00
  • no it's not. I actually did something like this (for a test): Thread.Sleep(10000); if(ct.IsCancellationRequested) { ct.ThrowIfCancellationRequested(); } And I'm not that slow ;-) The line with the throw is reached. – user369122 Sep 11 '13 at 11:48