6

I tried to run a simple example on the cancellation of a task like the one below

CancellationTokenSource tokenSource2 = new CancellationTokenSource();

CancellationToken token2 = tokenSource2.Token;


Task task2 = new Task(() =>
{
    for (int i = 0; i < int.MaxValue; i++)
    {
        token2.ThrowIfCancellationRequested();
        Thread.Sleep(100);
        Console.WriteLine("Task 2 - Int value {0}", i);
    }
}, token2);

task2.Start();

Console.WriteLine("Press any key to cancel the task");
Console.ReadLine();

tokenSource2.Cancel();
Console.WriteLine("Task 2 cancelled? {0}", task2.IsCanceled);

I expected that Console.WriteLine("Task 2 cancelled? {0}", task2.IsCanceled); would print **"Task 2 cancelled? True"**, but it printed "False".

Do you know what happened? Is that the expected behaviour? Thanks.

EDIT: to ensure that the task hasn't completed before the cancellation request is called. I added the Console.ReadLine().

Ian R. O'Brien
  • 6,682
  • 9
  • 45
  • 73
Toan Nguyen
  • 11,263
  • 5
  • 43
  • 59

3 Answers3

5

First, maybe you misunderstand what IsCanceled means? It doesn't mean “this Task is pending cancellation, so it should complete shortly”, it means “this Task has been canceled, it is now complete”.

If you didn't misunderstand that, think about what exactly the sequence of events is. What happens is this:

  1. ThrowIfCancellationRequested() is called, but the token wasn't canceled yet, so it doesn't throw.
  2. Thread.Sleep() is called, so the thread running the Task sleeps.
  3. Cancel() is called.
  4. IsCanceled is checked. The code in the Task didn't have a chance to realize that the token was canceled, so it's still running, so IsCanceled returns false.
  5. ThrowIfCancellationRequested() is called again, this time it throws, which actually cancels the Task.

This is why ISCanceled is returning false to you. If you want it to return true, you could add something like Thread.Sleep(150) before checking IsCanceled, or, even better, actually wait for the Task to complete.

svick
  • 236,525
  • 50
  • 385
  • 514
2

The Task ended before you call for cancellation, take a look at the following below this may help to shed some more light on how to resolve your issue

By reading from here http://social.msdn.microsoft.com/Forums/da-DK/parallelextensions/thread/9f88132a-f8bd-4885-ab63-645d7b6c2127 it seems that the token is used to cancel the task BEFORE the task is "really" started, but after it has been queued.

It's more a way to cancel a task that's scheduled to occur, but not started yet. Once the task is running, the only way to cancel it is cooperatively via your own checking within the method. Without this, you'd have to always start the task, then check it internally, which would add a lot of extra, unnecessary overhead

You can even read it from Cancellation token in Task constructor: why?

Community
  • 1
  • 1
MethodMan
  • 18,625
  • 6
  • 34
  • 52
2

This is not an answer but too long to write in a comment

CancellationTokenSource cancellationTokenSource = new CancellationTokenSource();
CancellationToken cancellationToken = cancellationTokenSource.Token;

Task task = new Task(() =>
{
    for (int i = 0; i < int.MaxValue; i++)
    {
        cancellationToken.ThrowIfCancellationRequested();
        Console.WriteLine("Task 2 - Int value {0}", i);
    }
}, cancellationToken);

task.Start();

cancellationTokenSource.Cancel();
try
{
    task.Wait();
}
catch (AggregateException ae)
{
    if(ae.InnerExceptions.Single() is TaskCanceledException)
        Console.WriteLine("Caught TaskCanceledException");
    else
        Console.WriteLine("Did not catch canceled");
}
Console.WriteLine("Task 2 cancelled? {0}", task.IsCanceled);
  • The code above prints what is expected, and I never got a print of 'Task 2 - Int value {0}' so I don't think it finished before canceling.
  • Note that canceling the task throws an exception in my sample
  • There are some patterns for handling exceptions using tasks that can be good to read about.
Johan Larsson
  • 17,112
  • 9
  • 74
  • 88