C# Linked Cancellation Token Source

Jason Ge
3 min readNov 16, 2023

--

Starting from the .NET Framework 4, Microsoft provides a unified model for cancellation of asynchronous or long-running synchronous operation:

CancellationTokenSource

The general pattern to implement the cancellation is:

  1. Create a CancellationTokenSource object
  2. Pass the Token property of the newly created CancellationTokenSource object to the async task
  3. Inside the async task, periodically check the value of the CancellationToken.IsCancellationRequested property. If it is true, that means user has cancelled the operation. You need to gracefully end the operation.
  4. In the main thread, you can cancel the async task by calling the CancellationTokenSource.Cancel() method. This would set the CancellationToken.IsCancellationRequested property to true.

Code Sample

Following code snippet demonstrated how to use the CancellationTokenSource. The form has two buttons: Start and Cancel. Clicking “Start” button would initialize a long running task. Clicking “Cancel” button would trigger the Cancel() method call of the CancellationTokenSource and set the IsCancellationRequested property to true. The long running task check this value every second and terminate when its value is true.

namespace WinFormsApp1
{
public partial class Form1 : Form
{
private CancellationTokenSource ctsDemo;
static Form1()
{
}

public Form1()
{
InitializeComponent();
}

private void btnStart_Click(object sender, EventArgs e)
{
ctsDemo = new CancellationTokenSource();
try
{
LongRunningTask(ctsDemo.Token);
}
catch (Exception ex)
{
Log(ex.Message);
}
}

private void Log(string message)
{
txtLog.BeginInvoke(new Action(() => txtLog.AppendText(message)));
}

private void LongRunningTask(CancellationToken token)
{
Cursor = Cursors.WaitCursor;
Task.Factory.StartNew(() =>
{
while (!token.IsCancellationRequested)
{
Log("Task running\r\n");

Task.Delay(TimeSpan.FromSeconds(1)).Wait();
}
Log("Task cancelled\r\n");
}, token);
}

private void btnCancel_Click(object sender, EventArgs e)
{
Cursor = Cursors.Default;
ctsDemo.Cancel();
ctsDemo.Dispose();
}
}
}

Automatically Set the IsCancellationRequested to True

You can automatically set the IsCancellationRequested property to true after certain amount of time by calling the CancellationTokenSource.CancelAfter(<time>). The framework would set the IsCancellationRequested property to true after the specified time passed.

Code Sample

The following code snippet set the cancellation time to 5 seconds. After 5 seconds, the framework would set the token’s IsCancellationRequested property to true therefore the task would end.

namespace WinFormsApp1
{
public partial class Form2 : Form
{
private CancellationTokenSource ctsDemo;

public Form2()
{
InitializeComponent();
}

private async void btnStart_Click(object sender, EventArgs e)
{
ctsDemo = new CancellationTokenSource();
ctsDemo.CancelAfter(TimeSpan.FromSeconds(5));
try
{
Cursor = Cursors.WaitCursor;
await LongRunningTask(ctsDemo.Token);
}
catch (Exception ex)
{
Log(ex.Message);
}
finally { Cursor = Cursors.Default; }
}

private Task LongRunningTask(CancellationToken token)
{
return Task.Factory.StartNew(() =>
{
int count = 0;
while (!token.IsCancellationRequested)
{
count++;
Log($"Task {Task.CurrentId} running {count}\r\n");
Task.Delay(TimeSpan.FromSeconds(1)).Wait();
}
Log("Task cancelled\r\n");
}, token);
}

private void Log(string message)
{
txtLog.BeginInvoke(new Action(() => txtLog.AppendText(message)));
}
}
}

Linked Cancellation Token Source

You can use CancellationTokenSource.CreateLinkedTokenSource(params CancellationToken[] tokens) method to create linked cancellation token source. The newly created cancellation token source will be in “Canceled” state when any of the source tokens are in “Canceled” state. This would be very useful if you have more than one tasks running. One of the tasks depends on the other tasks. If any of the other tasks failed or cancelled, you don’t want the dependable task to continue executing. In this case, you can use linked cancellation token source to automatically cancel the dependable task.

Code Sample

In following code snippet, we have created a main cancellation token source and use CreateLinkedTokenSource() method to create a linked cancellation token source.

When the main cancellation token source is cancelled, the linked source is also cancelled automatically. However, if you just cancel the linked token source, the main token source is not affected.

namespace WinFormsApp1
{
public partial class Form3 : Form
{
private CancellationTokenSource ctsMain;
private CancellationTokenSource ctsLinked;
public Form3()
{
InitializeComponent();
}

private void btnStart_Click(object sender, EventArgs e)
{
ctsMain = new CancellationTokenSource();
ctsLinked = CancellationTokenSource.CreateLinkedTokenSource(ctsMain.Token);
try
{
LongRunningTask(ctsMain.Token);
LongRunningTask(ctsLinked.Token);
}
catch (Exception ex)
{
Log(ex.Message);
}
}

private void Log(string message)
{
txtLog.BeginInvoke(new Action(() => txtLog.AppendText(message)));
}

private Task LongRunningTask(CancellationToken token)
{
return Task.Factory.StartNew(() =>
{
while (!token.IsCancellationRequested)
{
Log($"Task {Task.CurrentId} running\r\n");

Task.Delay(TimeSpan.FromSeconds(1)).Wait();
}
Log($"Task {Task.CurrentId} cancelled\r\n");
}, token);
}

private void btnCancel1_Click(object sender, EventArgs e)
{
ctsMain.Cancel();
}

private void btnCancel2_Click(object sender, EventArgs e)
{
ctsLinked.Cancel();
}
}
}

--

--

Jason Ge
Jason Ge

Written by Jason Ge

Software developer with over 20 years experience. Recently focus on Vue/Angular and asp.net core.

No responses yet