C#idiomatic C#
task-cancellationstandard/parallel-programming

Async and concurrency

Task cancellation

Understand task cancellation, which is supported in the Task and Task<TResult> classes through the use of cancellation tokens in .NET.

  • page standard/parallel-programming/task-cancellation
  • updated 10/20/2025

The System.Threading.Tasks.Task and System.Threading.Tasks.Task classes support cancellation by using cancellation tokens. For more information, see Cancellation in Managed Threads. In the Task classes, cancellation involves cooperation between the user delegate, which represents a cancelable operation, and the code that requested the cancellation. A successful cancellation involves the requesting code calling the System.Threading.CancellationTokenSource.Cancel method and the user delegate terminating the operation in a timely manner. You can terminate the operation by using one of these options:

The following example shows the basic pattern for task cancellation that throws the exception:

sample · csharpsamples/snippets/csharp/VS_Snippets_Misc/tpl_cancellation/cs/snippet02.cs02

csharp
//<snippet02>
using System;
using System.Threading;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        var tokenSource2 = new CancellationTokenSource();
        CancellationToken ct = tokenSource2.Token;

        var task = Task.Run(() =>
        {
            // Were we already canceled?
            ct.ThrowIfCancellationRequested();

            bool moreToDo = true;
            while (moreToDo)
            {
                // Poll on this property if you have to do
                // other cleanup before throwing.
                if (ct.IsCancellationRequested)
                {
                    // Clean up here, then...
                    ct.ThrowIfCancellationRequested();
                }
            }
        }, tokenSource2.Token); // Pass same token to Task.Run.

        tokenSource2.Cancel();

        // Just continue on this thread, or await with try-catch:
        try
        {
            await task;
        }
        catch (OperationCanceledException e)
        {
            Console.WriteLine($"{nameof(OperationCanceledException)} thrown with message: {e.Message}");
        }
        finally
        {
            tokenSource2.Dispose();
        }

        Console.ReadKey();
    }
}
//</snippet02>

sample · vbsamples/snippets/visualbasic/VS_Snippets_Misc/tpl_cancellation/vb/module1.vb02

vb
'<snippet02>
Imports System.Threading
Imports System.Threading.Tasks

Module Test
    Sub Main()
        Dim tokenSource2 As New CancellationTokenSource()
        Dim ct As CancellationToken = tokenSource2.Token

        Dim t2 = Task.Factory.StartNew(Sub()
                                           ' Were we already canceled?
                                           ct.ThrowIfCancellationRequested()

                                           Dim moreToDo As Boolean = True
                                           While moreToDo = True
                                               ' Poll on this property if you have to do
                                               ' other cleanup before throwing.
                                               If ct.IsCancellationRequested Then

                                                   ' Clean up here, then...
                                                   ct.ThrowIfCancellationRequested()
                                               End If

                                           End While
                                       End Sub _
        , tokenSource2.Token) ' Pass same token to StartNew.

        ' Cancel the task.
        tokenSource2.Cancel()

        ' Just continue on this thread, or Wait/WaitAll with try-catch:
        Try
            t2.Wait()

        Catch e As AggregateException

            For Each item In e.InnerExceptions
                Console.WriteLine(e.Message & " " & item.Message)
            Next
        Finally
            tokenSource2.Dispose()
        End Try

        Console.ReadKey()
    End Sub
End Module
'</snippet02>

For a complete example, see How to: Cancel a Task and Its Children.

When a task instance observes an System.OperationCanceledException thrown by the user code, it compares the exception's token to its associated token (the one that was passed to the API that created the Task). If the tokens are same and the token's System.Threading.CancellationToken.IsCancellationRequested property returns true, the task interprets this as acknowledging cancellation and transitions to the Canceled state. If you don't use a System.Threading.Tasks.Task.Wait or System.Threading.Tasks.Task.WaitAll method to wait for the task, then the task just sets its status to System.Threading.Tasks.TaskStatus.Canceled.

If you're waiting on a Task that transitions to the Canceled state, a System.Threading.Tasks.TaskCanceledException exception (wrapped in an System.AggregateException exception) is thrown. This exception indicates successful cancellation instead of a faulty situation. Therefore, the task's System.Threading.Tasks.Task.Exception property returns null.

If the token's System.Threading.CancellationToken.IsCancellationRequested property returns false or if the exception's token doesn't match the Task's token, the System.OperationCanceledException is treated like a normal exception, causing the Task to transition to the Faulted state. The presence of other exceptions will also cause the Task to transition to the Faulted state. You can get the status of the completed task in the System.Threading.Tasks.Task.Status property.

It's possible that a task might continue to process some items after cancellation is requested.

See also