【从零开始的C# CancellationToken】:一步步实现自定义取消令牌
发布时间: 2024-10-21 10:32:20 阅读量: 49 订阅数: 29
![CancellationToken](https://dotnettutorials.net/wp-content/uploads/2022/06/word-image-26786-7.png)
# 1. C# CancellationToken简介与基本用法
## 1.1 CancellationToken是什么?
CancellationToken是一种用于通知操作是否已被取消的机制。它通常用于异步编程中,以优雅地停止正在进行的工作。在.NET环境中,CancellationToken广泛应用于多线程和异步操作中,比如*** Core、Entity Framework Core等框架。
## 1.2 CancellationToken的主要用途
在多线程环境中,我们经常会遇到需要中断线程执行的场景。例如,在一个Web服务器上,当用户关闭浏览器时,服务器端的线程就应该停止响应。CancellationToken正好解决了这类问题,允许我们在不需要等待后台任务完成的情况下,立即中断线程的执行。
## 1.3 CancellationToken的基本用法
CancellationToken可以通过CancellationTokenSource生成,并在需要的时候传递给异步操作。以下是一个简单的示例代码:
```csharp
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
async Task MyAsyncTask(CancellationToken token)
{
while (!token.IsCancellationRequested)
{
// 执行任务
}
// 处理取消逻辑
}
// 在需要取消时
cts.Cancel();
```
在这个例子中,`CancellationTokenSource`对象用于产生一个令牌,该令牌传递给异步方法`MyAsyncTask`。当执行取消操作时,`CancellationTokenSource`的`Cancel`方法被调用,设置令牌状态为已取消,异步方法中可以通过检查`IsCancellationRequested`属性来响应取消请求。
CancellationToken的引入,大幅提高了.NET应用的可响应性和资源利用率,尤其是当涉及到大量的后台处理或网络请求时。在后续章节中,我们将深入探讨 CancellationToken 的源码分析、实践应用、高级用法以及它在未来应用中的发展趋势。
# 2. 深入 CancellationToken 源码分析
CancellationToken 是.NET框架中用于控制异步操作取消的一个重要组件。它提供了一种标准化的取消机制,允许开发者能够在多个线程、任务或异步操作中传递取消请求。深入理解其内部机制、生命周期管理以及高级特性,能够帮助开发者更高效地编写出健壮的异步代码。
## 2.1 CancellationToken 结构的内部机制
### 2.1.1 CancellationToken 的主要属性和方法
CancellationToken 是一个轻量级的结构体,它包含了几个关键的属性和方法,以便于操作和管理取消状态。
```csharp
public struct CancellationToken
{
private volatile int _CancellationRequested;
// Other fields...
public bool IsCancellationRequested
{
get { return _CancellationRequested != 0; }
}
public CancellationTokenSource CancellationTokenSource
{
get { return _CancellationTokenSource; }
}
// Other properties...
public void ThrowIfCancellationRequested()
{
// Implementation...
}
// Other methods...
}
```
在上面的代码中,`_CancellationRequested` 是一个表示取消请求状态的字段。当这个字段为非零值时,`IsCancellationRequested` 属性会返回 true。开发者通常会检查这个属性来确定是否需要提前终止异步操作。`CancellationTokenSource` 属性允许访问与此 CancellationToken 关联的 CancellationTokenSource,提供了一种机制来追踪和管理 CancellationToken 的来源。
### 2.1.2 CancellationTokenSource 的关键功能
CancellationTokenSource 是 CancellationToken 的主要提供者和管理器,它提供了用于注册取消回调、触发取消以及判断取消状态的方法。
```csharp
public class CancellationTokenSource : IDisposable
{
private volatile CancellationToken _token;
// Other fields...
public CancellationToken Token
{
get { return _token; }
}
public void Cancel()
{
// Implementation...
}
public void CancelAfter(TimeSpan delay)
{
// Implementation...
}
public void Dispose()
{
// Implementation...
}
// Other methods...
}
```
在上述代码中,`Token` 属性返回一个 CancellationToken 实例,使得用户能够在调用取消操作时得到一个令牌。`Cancel` 方法用于立即触发取消操作,而 `CancelAfter` 方法则用于在指定延迟之后自动触发取消。
## 2.2 CancellationToken 生命周期管理
### 2.2.1 注册与注销回调机制
CancellationToken 支持注册回调函数,这些函数会在取消令牌被触发时执行。开发者可以利用这个机制来执行清理或释放资源的操作。
```csharp
var cts = new CancellationTokenSource();
cts.Token.Register(() =>
{
Console.WriteLine("Operation was cancelled");
});
// Some asynchronous operation...
cts.Cancel(); // This will cause the callback to be executed.
```
在上面的代码示例中,我们创建了一个 CancellationTokenSource 实例,并注册了一个回调。如果后续调用了 `Cancel` 方法,注册的回调将会被执行。
### 2.2.2 CancellationToken 的状态流转
CancellationToken 具有一个简单的状态流转逻辑,其状态从初始的未请求(`IsCancellationRequested` 为 false)变为已请求(`IsCancellationRequested` 为 true)。状态的流转是单向且不可逆的。
```mermaid
graph TD;
A[初始化] --> B[待取消];
B --> C[取消中];
C --> D[已取消];
```
状态的每一次变化都伴随着 CancellationToken 源码内部机制的一系列操作,确保了取消请求的正确传递和处理。
## 2.3 CancellationToken 源码高级特性解读
### 2.3.1 源码中的并发控制和异常处理
CancellationToken 源码中使用了诸如 CAS (Compare-And-Swap) 操作来处理并发控制问题,确保状态的更改是原子性的。
```csharp
private static int VolatileRead(ref int address)
{
return Volatile.Read(ref address);
}
private bool CAS(int comparand, int value)
{
***pareExchange(ref _CancellationRequested, value, comparand) == comparand;
}
```
在异常处理方面,源码通过 `Try` 模式确保即使在取消过程中发生异常,整个取消流程也不会受到影响,异常会被记录但不会阻止其他订阅者继续执行。
### 2.3.2 源码中的取消操作的原子性分析
取消操作的原子性是通过确保状态的更改在逻辑上是一致的,没有其他线程能够在此操作中间插入其他逻辑来破坏这一状态。
```csharp
public void Cancel()
{
if (!CAS(0, CancellationState.CancelRequested))
return;
// Invoke callbacks...
// Signal to other threads...
}
```
上述代码展示了 Cancel 方法的原子操作逻辑,`CAS` 方法确保了从设置状态到执行回调的整个过程要么全部完成,要么都不执行。这种原子性的保障对于实现线程安全的取消机制至关重要。
# 3. CancellationToken 实践应用
在深入理解 CancellationToken 的基础之后,我们自然会转向如何在实际开发中应用 CancellationToken。本章节将着重介绍在不同场景下 CancellationToken 的应用,包括同步与异步任务的取消操作,以及在不同框架中的集成方法。同时,我们也会探讨自定义 CancellationTokenSource 扩展的实践。
## 3.1 任务取消操作的实现与示例
在处理长时间运行的任务时,适时地取消任务是非常重要的功能。使用 CancellationToken 可以优雅地取消这些任务,而不影响应用程序的其他部分。
### 3.1.1 同步任务的取消实践
在同步任务中,我们可以使用 CancellationToken 的 `ThrowIfCancellationRequested` 方法来抛出 `OperationCanceledException`,从而取消任务。以下是一个同步任务取消的简单示例:
```csharp
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static void Main(string[] args)
{
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
try
{
// 模拟长时间运行的任务
for (int i = 0; i < 100; i++)
{
if (token.IsCancellationRequested)
{
token.ThrowIfCancellationRequested();
}
Console.WriteLine(i);
Thread.Sleep(100); // 模拟耗时操作
}
}
catch (OperationCanceledException)
{
Console.WriteLine("任务被取消");
}
Console.WriteLine("任务完成");
}
}
```
在上述代码中,我们创建了一个 `CancellationTokenSource` 实例,并从中获取了 `CancellationToken`。在循环中,我们检查 `token.IsCancellationRequested` 属性,以确定是否收到了取消请求。如果已收到请求,我们将调用 `token.ThrowIfCancellationRequested()` 来抛出异常,这将中断任务的执行。
### 3.1.2 异步任务的取消实践
异步任务的取消则更为常见和重要,我们可以结合 `async` 和 `await` 关键字来实现这一点:
```csharp
using System;
using System.Threading;
using System.Threading.Tasks;
class Program
{
static async Task Main(string[] args)
{
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
try
{
// 异步任务
await DoWorkAsync(token);
}
catch (OperationCanceledException)
{
Console.WriteLine("异步任务被取消");
}
}
static async Task DoWorkAsync(CancellationToken token)
{
for (int i = 0; i < 100; i++)
{
if (token.IsCancellationRequested)
{
throw new OperationCanceledException(token);
}
Console.WriteLine(i);
await Task.Delay(100, token); // 模拟耗时操作
}
}
}
```
在上述代码中,`DoWorkAsync` 方法通过检查 `token.IsCancellationRequested` 属性来判断是否需要取消异步操作。如果需要取消,
0
0