CancellationToken with Task.Run and Wait

Cancelling tasks in .NET means using CancellationToken but then how the token is used really matters.

Consider the following code – from a Console app.

public static void Main(string[] args)
{
    var cts = new CancellationTokenSource();
    var token = cts.Token;
    cts.CancelAfter(3000);

    try
    {
        Task.Run(() =>
            {
                DoSomething(token);
            }, token)
            .Wait(token);
    }
    catch (Exception e)
    {
        //throw;
    }
}

CancellationToken is used in lines 11, 12, and 13 in the preceding code. The token used in all three places is the same token. When the CancelAfter method is called on the CTS to signal cancellation, what happens?

Line 13 – Wait(token)

This cancels only the process of waiting for the task, without actually cancelling the task itself. DoSomething will continue to run even after the Cancel method is called. Of course, that will result in OperationCanceledException being thrown, which gets caught in the catch block but the method happily runs.

Line 11 – DoSomething(token)

This stops the execution of DoSomething, provided the method is written to honor cancellation requests. For example, the following implementation ignores the token and does what it wants to do.

public static void DoSomething(CancellationToken t)
{
    Thread.Sleep(10000);
}

On the other hand, the following implementation checks for cancellation periodically and throws OperationCanceledException when cancellation is requested.

public static void DoSomething(CancellationToken t)
{
    for (int i = 0; i < 100; i++)
    {
        Thread.Sleep(100);
        t.ThrowIfCancellationRequested();
    }
}

Line 12 – Task.Run(delegate, token)

If cancellation is requested before the task starts to execute, Task will not execute and task status will immediately become canceled. Task is never started in this case.

If cancellation is requested after the task has started to run, calling Cancel cannot influence DoSomething. However, there is something interesting to note.

Consider the following Main method, rewritten slightly. The cancellation token is passed only to the DoSomething method but not Task.Run.

public static void Main(string[] args)
{
    var cts = new CancellationTokenSource();
    var token = cts.Token;
    cts.CancelAfter(3000);

    Task task;
    try
    {
        task = Task.Run(() =>
        {
            DoSomething(token);
        });

        task.Wait();

    }
    catch (Exception e)
    {
        //throw;
    }
}

With that, when DoSomething throws a OperationCanceledException in response to the cancellation, the task becomes faulted. On the other hand, if you pass the same token to Task.Start, as shown by the line 6 in the following code, the task becomes canceled, instead of faulted.

try
{
    task = Task.Run(() =>
    {
        DoSomething(token);
    }, token);

    task.Wait();

}
catch (Exception e)
{
    //throw;
}
Advertisements

2 thoughts on “CancellationToken with Task.Run and Wait

  1. Typically it is done within the method periodically. See the second implementation of DoSomething.

    public static void DoSomething(CancellationToken t)
    {
    for (int i = 0; i < 100; i++)
    {
    Thread.Sleep(100);
    t.ThrowIfCancellationRequested();
    }
    }

  2. Very nice article. Should I check for t.ThrowIfCancellationRequested(); before the method code executes ? or should be at the end ? Please guide

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s