CancellationTokenSource,如何巧妙利用CancellationTokenSource来优雅地取消异步操作


`CancellationTokenSource` 是 .NET 框架中用于创建、管理和取消 `CancellationToken` 的类。`CancellationToken` 本身是一个值类型,它表示一个取消操作的请求,而 `CancellationTokenSource` 是这个请求的源头。在异步操作中,使用 `CancellationTokenSource` 可以优雅地取消正在进行的操作,而不需要中断或中止整个方法或线程。

下面将详细解释如何巧妙利用 `CancellationTokenSource` 来优雅地取消异步操作:

1. 创建 `CancellationTokenSource`

你需要创建一个 `CancellationTokenSource` 实例。这个实例将用于生成 `CancellationToken`。

csharp

using System.Threading;

// 创建一个 CancellationTokenSource

var cts = new CancellationTokenSource();

// 获取 CancellationToken

var token = cts.Token;

2. 将 `CancellationToken` 传递给异步操作

将 `CancellationToken` 传递给启动的异步操作。这通常通过 `async` 方法中的参数来实现。

csharp

public async Task DoWorkAsync(CancellationToken token)

{

try

{

// 模拟长时间运行的任务

for (int i = 0; i < 10; i++)

{

await Task.Delay(1000, token);

// 检查是否收到取消请求

if (token.IsCancellationRequested)

{

// 抛出 OperationCanceledException,表示操作被取消

throw new TaskCanceledException("Operation canceled by user.");

}

Console.WriteLine($"Working... {i}");

}

}

catch (OperationCanceledException)

{

Console.WriteLine("Task was canceled.");

}

catch (Exception ex)

{

Console.WriteLine($"An error occurred: {ex.Message}");

}

}

3. 使用 `CancellationTokenSource` 取消操作

当需要取消异步操作时,调用 `CancellationTokenSource` 的 `Cancel` 方法。

csharp

// 调用异步方法,传入 CancellationToken

var task = DoWorkAsync(token);

// 在需要取消的时候调用 Cancel 方法

// 例如,在按钮点击事件中

// cts.Cancel();

4. 在异步操作中处理取消请求

在异步操作内部,定期检查 `CancellationToken` 的 `IsCancellationRequested` 属性,并据此决定是否继续执行。

csharp

if (token.IsCancellationRequested)

{

// 抛出 OperationCanceledException,表示操作被取消

throw new TaskCanceledException("Operation canceled by user.");

}

5. 捕获 `OperationCanceledException`

在调用异步方法的代码中,使用 `try/catch` 块来捕获 `OperationCanceledException`,并据此处理取消操作。

csharp

try

{

await task;

}

catch (OperationCanceledException)

{

Console.WriteLine("Task was canceled.");

}

catch (Exception ex)

{

Console.WriteLine($"An error occurred: {ex.Message}");

}

6. 释放 `CancellationTokenSource` 资源

当你不再需要 `CancellationTokenSource` 时,调用其 `Dispose` 方法来释放资源。

csharp

// 释放资源

cts.Dispose();

示例

下面是一个完整的示例,展示了如何使用 `CancellationTokenSource` 来取消异步操作:

csharp

using System;

using System.Threading.Tasks;

class Program

{

static async Task Main(string[] args)

{

// 创建一个 CancellationTokenSource

var cts = new CancellationTokenSource();

// 获取 CancellationToken

var token = cts.Token;

// 调用异步方法,传入 CancellationToken

var task = DoWorkAsync(token);

// 在 Main 方法中等待一段时间,然后取消操作

await Task.Delay(5000);

// 取消操作

cts.Cancel();

// 等待异步操作完成

try

{

await task;

}

catch (OperationCanceledException)

{

Console.WriteLine("Task was canceled.");

}

catch (Exception ex)

{

Console.WriteLine($"An error occurred: {ex.Message}");

}

// 释放资源

cts.Dispose();

}

static async Task DoWorkAsync(CancellationToken token)

{

try

{

// 模拟长时间运行的任务

for (int i = 0; i < 10; i++)

{

await Task.Delay(1000, token);

// 检查是否收到取消请求

if (token.IsCancellationRequested)

{

// 抛出 OperationCanceledException,表示操作被取消

throw new TaskCanceledException("Operation canceled by user.");

}

Console.WriteLine($"Working... {i}");

}

}

catch (OperationCanceledException)

{

Console.WriteLine("Task was canceled.");

}

catch (Exception ex)

{

Console.WriteLine($"An error occurred: {ex.Message}");

}

}

}

在这个示例中,`DoWorkAsync` 方法模拟了一个长时间运行的异步操作,通过 `CancellationToken` 来检查是否收到取消请求。在 `Main` 方法中,我们等待一段时间然后取消操作,并捕获 `OperationCanceledException` 来处理取消操作。

注意事项

- 当你调用 `CancellationTokenSource.Cancel` 方法时,它不会立即停止正在进行的操作,而是向操作发送一个取消请求。操作应该定期检查这个请求,并据此决定是否继续执行。

- 如果操作在收到取消请求后选择抛出 `OperationCanceledException`,那么调用方可以通过捕获这个异常来优雅地处理取消操作。

- 当你不再需要 `CancellationTokenSource` 时,应该调用其 `Dispose` 方法来释放资源。

- 在使用 `CancellationTokenSource` 时,请确保在适当的时候取消操作,以避免资源泄漏或长时间运行不必要的操作。

- 当你调用 `CancellationTokenSource.Cancel` 方法后,`IsCancellationRequested` 属性将返回 `true`,表示已经发出取消请求。

- `CancellationTokenSource` 是线程安全的,可以在多个线程上安全地访问和取消操作。

通过巧妙地使用 `CancellationTokenSource`,你可以优雅地取消异步操作,而不需要中断整个方法或线程。这有助于提高代码的可维护性和健壮性。