Jens Marchewka
Jens Marchewka

Reputation: 1443

Task not Cancelling as expected

We got the following scenario:

class Program
{
    static void Main(string[] args)
    {
        // trigger the delayed function
        trigger();

        // cancel the running task.
        _token.Cancel();

        // keep window open ;-)
        Console.ReadLine();

    }

    private static CancellationTokenSource _token = null;
    private static async void trigger()
    {
        _token = new CancellationTokenSource();

        try
        {
            // run task
            await Task.Run(async () =>
            {
                // wait time
                await Task.Delay(2500);

                // we should be cancelled here !!
                Console.WriteLine(string.Format("IsCancellationRequested={0}", _token.Token.IsCancellationRequested));
                Console.WriteLine("SHOULD NOT HAPPEN");

            }, _token.Token);
        }
        catch (TaskCanceledException)
        {
        }
    }
}

IMO the expected behaviour is that the operation of the task is cancelled mostly after the Task.Delay(2500) is processed.

But the console is printing:

IsCancellationRequested=True
SHOULD NOT HAPPEN

This just feels like a bug. If you add the CancellationToken as parameter to the Task.Delay-Function it will work as expected.

So how to handle a cancellation if a function inside the task uses Task.Delay, that you may not know?

Upvotes: 1

Views: 3829

Answers (2)

i3arnon
i3arnon

Reputation: 116518

Cancellation in .Net is cooperative. Passing a token as parameter to Task.Run only associates the token with the returned task.

To actually cancel the task you need to check the token inside the task itself. If you want the task to be canceled while inside the delay you'll need to pass the token to the Task.Delay method. Otherwise you can only check while before or after the delay:

await Task.Run(async () =>
{
    _token.Token.ThrowIfCancellationRequested();
    await Task.Delay(2500, _token.Token);
    _token.Token.ThrowIfCancellationRequested();
    
    Console.WriteLine(string.Format("IsCancellationRequested={0}", _token.Token.IsCancellationRequested));
    Console.WriteLine("SHOULD NOT HAPPEN");

}, _token.Token);

Upvotes: 3

Volkan Paksoy
Volkan Paksoy

Reputation: 6937

Here's a nice MSDN article: https://msdn.microsoft.com/en-us/library/dd537607(v=vs.110).aspx

The calling thread does not forcibly end the task; it only signals that cancellation is requested. If the task is already running, it is up to the user delegate to notice the request and respond appropriately. If cancellation is requested before the task runs, then the user delegate is never executed and the task object transitions into the Canceled state.

As i3arnon suggested you can check the state and throw an exception before carrying out the actual work. Code excerpt from the article:

...
  // Was cancellation already requested?  
      if (ct.IsCancellationRequested == true) {
         Console.WriteLine("Task {0} was cancelled before it got started.",
                           taskNum);
         ct.ThrowIfCancellationRequested();
      } 
...

Upvotes: 1

Related Questions