Task 取消操作
知识点
取消机制基础
- CancellationToken:表示取消请求的令牌,用于检查是否有取消请求
- CancellationTokenSource:取消令牌的源,用于发起取消请求
- 协作式取消:任务需要主动检查取消令牌并响应取消请求
- OperationCanceledException:取消操作时抛出的标准异常
取消模式
- 轮询检查:定期检查
CancellationToken.IsCancellationRequested
- 抛出异常:调用
CancellationToken.ThrowIfCancellationRequested()
- 取消回调:使用
CancellationToken.Register() 注册取消回调
- 超时取消:使用
CancellationTokenSource.CancelAfter() 设置超时
取消传播
- 任务链取消:取消可以沿着任务链传播
- 并行取消:多个任务可以响应同一个取消令牌
- 嵌套取消:子取消令牌可以与父取消令牌关联
- 组合取消:多个取消条件的组合
代码样例
基本取消操作
using System;
using System.Threading;
using System.Threading.Tasks;
public class BasicCancellationDemo
{
public static void DemonstrateBasicCancellation()
{
Console.WriteLine("=== 基本取消操作演示 ===");
// 1. 简单的任务取消
Console.WriteLine("1. 简单的任务取消:");
DemonstrateSimpleTaskCancellation();
// 2. 带返回值的任务取消
Console.WriteLine("\n2. 带返回值的任务取消:");
DemonstrateTaskWithResultCancellation();
// 3. 超时取消
Console.WriteLine("\n3. 超时取消:");
DemonstrateTimeoutCancellation();
Console.WriteLine("基本取消操作演示完成\n");
}
private static void DemonstrateSimpleTaskCancellation()
{
using (var cts = new CancellationTokenSource())
{
Task task = Task.Run(() =>
{
Console.WriteLine(" 任务开始执行...");
for (int i = 0; i < 10; i++)
{
// 检查取消请求
cts.Token.ThrowIfCancellationRequested();
Console.WriteLine($" 工作进度: {i + 1}/10");
Thread.Sleep(500);
}
Console.WriteLine(" 任务正常完成");
}, cts.Token);
// 2秒后请求取消
Task.Run(async () =>
{
await Task.Delay(2000);
Console.WriteLine(" 发起取消请求...");
cts.Cancel();
});
try
{
task.Wait();
Console.WriteLine(" 任务成功完成");
}
catch (AggregateException ex) when (ex.InnerException is OperationCanceledException)
{
Console.WriteLine(" 任务被取消");
}
Console.WriteLine($" 任务最终状态: {task.Status}");
}
}
private static void DemonstrateTaskWithResultCancellation()
{
using (var cts = new CancellationTokenSource())
{
Task<int> calculationTask = Task.Run(() =>
{
Console.WriteLine(" 开始复杂计算...");
int result = 0;
for (int i = 0; i < 1000000; i++)
{
// 每1000次迭代检查一次取消
if (i % 1000 == 0)
{
cts.Token.ThrowIfCancellationRequested();
}
result += i;
// 每100000次迭代显示进度
if (i % 100000 == 0)
{
Console.WriteLine($" 计算进度: {i / 10000}%");
}
}
Console.WriteLine(" 计算完成");
return result;
}, cts.Token);
// 1.5秒后取消
cts.CancelAfter(1500);
try
{
int result = calculationTask.Result;
Console.WriteLine($" 计算结果: {result}");
}
catch (AggregateException ex) when (ex.InnerException is OperationCanceledException)
{
Console.WriteLine(" 计算被取消");
}
Console.WriteLine($" 计算任务状态: {calculationTask.Status}");
}
}
private static void DemonstrateTimeoutCancellation()
{
Console.WriteLine(" 演示不同超时时间的效果:");
var timeouts = new[] { 500, 1500, 3000 };
foreach (int timeout in timeouts)
{
Console.WriteLine($"\n 测试 {timeout}ms 超时:");
using (var cts = new CancellationTokenSource())
{
Task<string> task = Task.Run(() =>
{
Console.WriteLine($" 任务开始 (预计运行2秒)");
for (int i = 0; i < 20; i++)
{
cts.Token.ThrowIfCancellationRequested();
Thread.Sleep(100);
}
return "任务完成";
}, cts.Token);
// 设置超时
cts.CancelAfter(timeout);
try
{
string result = task.Result;
Console.WriteLine($" 结果: {result}");
}
catch (AggregateException ex) when (ex.InnerException is OperationCanceledException)
{
Console.WriteLine($" 任务在 {timeout}ms 后被取消");
}
}
}
}
}
高级取消模式
public class AdvancedCancellationDemo
{
public static void DemonstrateAdvancedCancellation()
{
Console.WriteLine("=== 高级取消模式演示 ===");
// 1. 取消回调
Console.WriteLine("1. 取消回调:");
DemonstrateCancellationCallbacks();
// 2. 嵌套取消令牌
Console.WriteLine("\n2. 嵌套取消令牌:");
DemonstrateNestedCancellationTokens();
// 3. 多条件组合取消
Console.WriteLine("\n3. 多条件组合取消:");
DemonstrateCombinedCancellation();
Console.WriteLine("高级取消模式演示完成\n");
}
private static void DemonstrateCancellationCallbacks()
{
using (var cts = new CancellationTokenSource())
{
// 注册取消回调
var registration1 = cts.Token.Register(() =>
{
Console.WriteLine(" 回调1: 清理资源A");
});
var registration2 = cts.Token.Register(() =>
{
Console.WriteLine(" 回调2: 清理资源B");
});
var registration3 = cts.Token.Register(state =>
{
Console.WriteLine($" 回调3: 清理 {state}");
}, "自定义资源");
Task longRunningTask = Task.Run(() =>
{
Console.WriteLine(" 长时间运行的任务开始...");
try
{
for (int i = 0; i < 50; i++)
{
cts.Token.ThrowIfCancellationRequested();
Thread.Sleep(100);
if (i % 10 == 0)
{
Console.WriteLine($" 任务进度: {i}/50");
}
}
Console.WriteLine(" 任务正常完成");
}
catch (OperationCanceledException)
{
Console.WriteLine(" 任务收到取消信号");
throw;
}
}, cts.Token);
// 2秒后取消
Task.Run(async () =>
{
await Task.Delay(2000);
Console.WriteLine(" 发起取消请求...");
cts.Cancel();
});
try
{
longRunningTask.Wait();
}
catch (AggregateException ex) when (ex.InnerException is OperationCanceledException)
{
Console.WriteLine(" 任务已被取消");
}
// 清理注册
registration1.Dispose();
registration2.Dispose();
registration3.Dispose();
}
}
private static void DemonstrateNestedCancellationTokens()
{
using (var parentCts = new CancellationTokenSource())
using (var childCts = new CancellationTokenSource())
{
// 创建组合的取消令牌
using (var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(
parentCts.Token, childCts.Token))
{
Task task = Task.Run(() =>
{
Console.WriteLine(" 组合取消任务开始...");
for (int i = 0; i < 100; i++)
{
// 检查组合的取消令牌
combinedCts.Token.ThrowIfCancellationRequested();
Thread.Sleep(50);
if (i % 20 == 0)
{
Console.WriteLine($" 进度: {i}%");
}
}
Console.WriteLine(" 任务完成");
}, combinedCts.Token);
// 随机选择父级或子级取消
Task.Run(async () =>
{
await Task.Delay(1500);
if (new Random().Next(2) == 0)
{
Console.WriteLine(" 父级取消源发起取消...");
parentCts.Cancel();
}
else
{
Console.WriteLine(" 子级取消源发起取消...");
childCts.Cancel();
}
});
try
{
task.Wait();
Console.WriteLine(" 任务成功完成");
}
catch (AggregateException ex) when (ex.InnerException is OperationCanceledException)
{
Console.WriteLine(" 任务被取消");
Console.WriteLine($" 父级是否取消: {parentCts.Token.IsCancellationRequested}");
Console.WriteLine($" 子级是否取消: {childCts.Token.IsCancellationRequested}");
Console.WriteLine($" 组合是否取消: {combinedCts.Token.IsCancellationRequested}");
}
}
}
}
private static void DemonstrateCombinedCancellation()
{
using (var userCts = new CancellationTokenSource()) // 用户取消
using (var timeoutCts = new CancellationTokenSource()) // 超时取消
using (var systemCts = new CancellationTokenSource()) // 系统取消
{
// 创建组合取消令牌
using (var combinedCts = CancellationTokenSource.CreateLinkedTokenSource(
userCts.Token, timeoutCts.Token, systemCts.Token))
{
Task<string> task = Task.Run(() =>
{
Console.WriteLine(" 多条件取消任务开始...");
for (int i = 0; i < 60; i++)
{
combinedCts.Token.ThrowIfCancellationRequested();
Thread.Sleep(100);
if (i % 15 == 0)
{
Console.WriteLine($" 运行时间: {i / 10.0}秒");
}
}
return "任务成功完成";
}, combinedCts.Token);
// 设置不同的取消条件
timeoutCts.CancelAfter(3000); // 3秒超时
// 模拟用户在2秒后取消
Task.Run(async () =>
{
await Task.Delay(2000);
if (new Random().Next(3) == 0)
{
Console.WriteLine(" 用户请求取消...");
userCts.Cancel();
}
});
// 模拟系统在2.5秒后取消
Task.Run(async () =>
{
await Task.Delay(2500);
if (new Random().Next(3) == 0)
{
Console.WriteLine(" 系统请求取消...");
systemCts.Cancel();
}
});
try
{
string result = task.Result;
Console.WriteLine($" 结果: {result}");
}
catch (AggregateException ex) when (ex.InnerException is OperationCanceledException)
{
Console.WriteLine(" 任务被取消,原因分析:");
if (userCts.Token.IsCancellationRequested)
Console.WriteLine(" - 用户取消");
if (timeoutCts.Token.IsCancellationRequested)
Console.WriteLine(" - 超时取消");
if (systemCts.Token.IsCancellationRequested)
Console.WriteLine(" - 系统取消");
}
}
}
}
}
异步方法中的取消
public class AsyncCancellationDemo
{
public static void DemonstrateAsyncCancellation()
{
Console.WriteLine("=== 异步方法中的取消演示 ===");
// 1. async/await 模式的取消
Console.WriteLine("1. async/await 模式的取消:");
DemonstrateAsyncAwaitCancellation().Wait();
// 2. 多级异步调用的取消传播
Console.WriteLine("\n2. 多级异步调用的取消传播:");
DemonstrateAsyncCancellationPropagation().Wait();
// 3. 并行异步操作的取消
Console.WriteLine("\n3. 并行异步操作的取消:");
DemonstrateParallelAsyncCancellation().Wait();
Console.WriteLine("异步方法取消演示完成\n");
}
private static async Task DemonstrateAsyncAwaitCancellation()
{
using (var cts = new CancellationTokenSource())
{
// 3秒后自动取消
cts.CancelAfter(3000);
try
{
string result = await PerformAsyncOperationAsync(cts.Token);
Console.WriteLine($" 异步操作结果: {result}");
}
catch (OperationCanceledException)
{
Console.WriteLine(" 异步操作被取消");
}
}
}
private static async Task<string> PerformAsyncOperationAsync(CancellationToken cancellationToken)
{
Console.WriteLine(" 开始异步操作...");
for (int i = 0; i < 10; i++)
{
// 异步等待,同时检查取消
await Task.Delay(500, cancellationToken);
Console.WriteLine($" 异步步骤 {i + 1}/10 完成");
}
return "异步操作完成";
}
private static async Task DemonstrateAsyncCancellationPropagation()
{
using (var cts = new CancellationTokenSource())
{
// 2秒后取消
cts.CancelAfter(2000);
try
{
await Level1AsyncMethod(cts.Token);
Console.WriteLine(" 多级异步调用完成");
}
catch (OperationCanceledException)
{
Console.WriteLine(" 多级异步调用被取消");
}
}
}
private static async Task Level1AsyncMethod(CancellationToken cancellationToken)
{
Console.WriteLine(" 级别1 异步方法开始");
await Task.Delay(300, cancellationToken);
await Level2AsyncMethod(cancellationToken);
Console.WriteLine(" 级别1 异步方法完成");
}
private static async Task Level2AsyncMethod(CancellationToken cancellationToken)
{
Console.WriteLine(" 级别2 异步方法开始");
await Task.Delay(400, cancellationToken);
await Level3AsyncMethod(cancellationToken);
Console.WriteLine(" 级别2 异步方法完成");
}
private static async Task Level3AsyncMethod(CancellationToken cancellationToken)
{
Console.WriteLine(" 级别3 异步方法开始");
for (int i = 0; i < 5; i++)
{
await Task.Delay(300, cancellationToken);
Console.WriteLine($" 级别3 步骤 {i + 1}/5");
}
Console.WriteLine(" 级别3 异步方法完成");
}
private static async Task DemonstrateParallelAsyncCancellation()
{
using (var cts = new CancellationTokenSource())
{
// 2.5秒后取消
cts.CancelAfter(2500);
try
{
// 创建多个并行的异步任务
Task[] tasks = new Task[]
{
CreateAsyncTask("任务A", 1000, cts.Token),
CreateAsyncTask("任务B", 1500, cts.Token),
CreateAsyncTask("任务C", 2000, cts.Token),
CreateAsyncTask("任务D", 3000, cts.Token)
};
await Task.WhenAll(tasks);
Console.WriteLine(" 所有并行任务完成");
}
catch (OperationCanceledException)
{
Console.WriteLine(" 并行任务被取消");
}
}
}
private static async Task CreateAsyncTask(string taskName, int duration, CancellationToken cancellationToken)
{
Console.WriteLine($" {taskName} 开始 (预计运行 {duration}ms)");
try
{
await Task.Delay(duration, cancellationToken);
Console.WriteLine($" {taskName} 完成");
}
catch (OperationCanceledException)
{
Console.WriteLine($" {taskName} 被取消");
throw; // 重新抛出,让调用者知道取消发生了
}
}
}
优雅关闭模式
public class GracefulShutdownDemo
{
public class DataProcessor
{
private readonly CancellationTokenSource _shutdownCts = new CancellationTokenSource();
private Task _processingTask;
private bool _isRunning = false;
public void Start()
{
if (_isRunning)
return;
_isRunning = true;
_processingTask = ProcessDataContinuously(_shutdownCts.Token);
Console.WriteLine("数据处理器已启动");
}
public async Task StopAsync(TimeSpan timeout = default)
{
if (!_isRunning)
return;
Console.WriteLine("开始优雅关闭数据处理器...");
// 发起取消请求
_shutdownCts.Cancel();
try
{
// 等待处理任务完成
if (timeout == default)
timeout = TimeSpan.FromSeconds(10);
await _processingTask.WaitAsync(timeout);
Console.WriteLine("数据处理器已优雅关闭");
}
catch (TimeoutException)
{
Console.WriteLine("等待超时,强制关闭数据处理器");
}
catch (OperationCanceledException)
{
Console.WriteLine("数据处理器已被取消");
}
_isRunning = false;
}
private async Task ProcessDataContinuously(CancellationToken cancellationToken)
{
int processedCount = 0;
try
{
while (!cancellationToken.IsCancellationRequested)
{
// 处理一批数据
await ProcessBatch(processedCount + 1, cancellationToken);
processedCount++;
// 检查是否有取消请求
if (cancellationToken.IsCancellationRequested)
{
Console.WriteLine("收到关闭信号,正在完成当前批次...");
break;
}
// 等待下一个批次,可以被取消中断
await Task.Delay(1000, cancellationToken);
}
}
catch (OperationCanceledException)
{
Console.WriteLine("数据处理被取消");
}
finally
{
// 清理资源
Console.WriteLine($"清理资源,共处理了 {processedCount} 个批次");
}
}
private async Task ProcessBatch(int batchNumber, CancellationToken cancellationToken)
{
Console.WriteLine($" 处理批次 {batchNumber}...");
// 模拟批次处理,支持取消
for (int i = 0; i < 5; i++)
{
cancellationToken.ThrowIfCancellationRequested();
await Task.Delay(200, cancellationToken);
}
Console.WriteLine($" 批次 {batchNumber} 处理完成");
}
public void Dispose()
{
_shutdownCts?.Cancel();
_shutdownCts?.Dispose();
}
}
public static void DemonstrateGracefulShutdown()
{
Console.WriteLine("=== 优雅关闭模式演示 ===");
var processor = new DataProcessor();
try
{
// 启动处理器
processor.Start();
// 运行一段时间
Console.WriteLine("让数据处理器运行 5 秒...");
Thread.Sleep(5000);
// 优雅关闭
processor.StopAsync(TimeSpan.FromSeconds(3)).Wait();
}
finally
{
processor.Dispose();
}
Console.WriteLine("优雅关闭演示完成\n");
}
}
实际应用场景
public class CancellationPracticalDemo
{
public class FileDownloader
{
public async Task<string> DownloadFileAsync(string url, string filePath,
IProgress<int> progress, CancellationToken cancellationToken)
{
Console.WriteLine($"开始下载: {url}");
try
{
// 模拟下载过程
for (int i = 0; i <= 100; i += 10)
{
// 检查取消
cancellationToken.ThrowIfCancellationRequested();
// 模拟下载延迟
await Task.Delay(200, cancellationToken);
// 报告进度
progress?.Report(i);
Console.WriteLine($" 下载进度: {i}%");
}
Console.WriteLine($"下载完成: {filePath}");
return filePath;
}
catch (OperationCanceledException)
{
Console.WriteLine($"下载被取消: {url}");
// 清理部分下载的文件
Console.WriteLine(" 清理部分下载的文件...");
throw;
}
}
}
public class BatchProcessor
{
public async Task<int> ProcessItemsAsync<T>(IEnumerable<T> items,
Func<T, CancellationToken, Task> processor,
CancellationToken cancellationToken)
{
int processedCount = 0;
var itemList = items.ToList();
Console.WriteLine($"开始批量处理 {itemList.Count} 个项目");
foreach (var item in itemList)
{
try
{
cancellationToken.ThrowIfCancellationRequested();
await processor(item, cancellationToken);
processedCount++;
Console.WriteLine($" 已处理: {processedCount}/{itemList.Count}");
}
catch (OperationCanceledException)
{
Console.WriteLine($"批量处理被取消,已处理 {processedCount} 个项目");
throw;
}
}
Console.WriteLine($"批量处理完成,共处理 {processedCount} 个项目");
return processedCount;
}
}
public static void DemonstratePracticalCancellation()
{
Console.WriteLine("=== 实际应用场景取消演示 ===");
// 1. 文件下载取消
Console.WriteLine("1. 文件下载取消:");
DemonstrateFileDownloadCancellation().Wait();
// 2. 批量处理取消
Console.WriteLine("\n2. 批量处理取消:");
DemonstrateBatchProcessingCancellation().Wait();
// 3. 服务请求超时取消
Console.WriteLine("\n3. 服务请求超时取消:");
DemonstrateServiceRequestCancellation().Wait();
Console.WriteLine("实际应用场景演示完成\n");
}
private static async Task DemonstrateFileDownloadCancellation()
{
var downloader = new FileDownloader();
using (var cts = new CancellationTokenSource())
{
var progress = new Progress<int>(percentage =>
{
if (percentage >= 50)
{
Console.WriteLine(" 下载进度达到50%,用户请求取消...");
cts.Cancel();
}
});
try
{
string result = await downloader.DownloadFileAsync(
"http://example.com/largefile.zip",
"largefile.zip",
progress,
cts.Token);
Console.WriteLine($" 下载成功: {result}");
}
catch (OperationCanceledException)
{
Console.WriteLine(" 文件下载已取消");
}
}
}
private static async Task DemonstrateBatchProcessingCancellation()
{
var processor = new BatchProcessor();
var items = Enumerable.Range(1, 10).ToArray();
using (var cts = new CancellationTokenSource())
{
// 3秒后取消
cts.CancelAfter(3000);
try
{
int processed = await processor.ProcessItemsAsync(
items,
async (item, cancellationToken) =>
{
Console.WriteLine($" 处理项目 {item}");
await Task.Delay(400, cancellationToken);
},
cts.Token);
Console.WriteLine($" 批量处理完成: {processed} 个项目");
}
catch (OperationCanceledException)
{
Console.WriteLine(" 批量处理已取消");
}
}
}
private static async Task DemonstrateServiceRequestCancellation()
{
var services = new[] { "ServiceA", "ServiceB", "ServiceC" };
foreach (string service in services)
{
using (var cts = new CancellationTokenSource())
{
// 为每个服务设置不同的超时时间
int timeout = service == "ServiceB" ? 800 : 1500; // ServiceB 超时更快
cts.CancelAfter(timeout);
try
{
string result = await CallServiceAsync(service, cts.Token);
Console.WriteLine($" {service} 调用成功: {result}");
}
catch (OperationCanceledException)
{
Console.WriteLine($" {service} 调用超时 ({timeout}ms)");
}
}
}
}
private static async Task<string> CallServiceAsync(string serviceName, CancellationToken cancellationToken)
{
Console.WriteLine($" 调用 {serviceName}...");
// 模拟不同的服务响应时间
int delay = serviceName switch
{
"ServiceA" => 500,
"ServiceB" => 1200, // 这个会超时
"ServiceC" => 600,
_ => 1000
};
await Task.Delay(delay, cancellationToken);
return $"来自 {serviceName} 的响应";
}
}
综合演示
public class ComprehensiveCancellationDemo
{
public static void RunAllDemos()
{
Console.WriteLine("Task 取消操作综合演示");
Console.WriteLine("=".PadRight(60, '='));
// 1. 基本取消操作
BasicCancellationDemo.DemonstrateBasicCancellation();
// 2. 高级取消模式
AdvancedCancellationDemo.DemonstrateAdvancedCancellation();
// 3. 异步方法中的取消
AsyncCancellationDemo.DemonstrateAsyncCancellation();
// 4. 优雅关闭模式
GracefulShutdownDemo.DemonstrateGracefulShutdown();
// 5. 实际应用场景
CancellationPracticalDemo.DemonstratePracticalCancellation();
Console.WriteLine("所有取消操作演示完成");
}
}
总结
取消机制的核心概念
- 协作式取消:任务需要主动检查并响应取消请求
- CancellationToken:轻量级的取消信号载体,可以安全地传递和检查
- CancellationTokenSource:取消信号的发起者,负责管理取消状态
- OperationCanceledException:标准的取消异常,用于表示操作被取消
取消检查的最佳实践
- 及时检查:在循环、长时间操作中定期检查取消状态
- ThrowIfCancellationRequested():简洁的取消检查和异常抛出
- Task.Delay(timeout, cancellationToken):在异步等待中支持取消
- Register 回调:注册取消时的清理逻辑
高级取消模式
- 组合取消令牌:使用 CreateLinkedTokenSource 组合多个取消条件
- 超时取消:CancelAfter() 方法设置自动超时
- 嵌套取消:子操作继承父操作的取消令牌
- 取消回调:在取消时执行清理逻辑
异步取消的注意事项
- 传递取消令牌:在异步调用链中一直传递 CancellationToken
- Task.Delay 取消:使用支持取消的 Task.Delay 重载
- 并行操作取消:确保所有并行任务都能响应取消
- 异常处理:正确处理 OperationCanceledException
优雅关闭设计
- 发起取消信号:通过 CancellationTokenSource.Cancel() 发起关闭
- 等待完成:给正在进行的操作一定时间完成
- 清理资源:在 finally 块或 using 语句中清理资源
- 超时处理:设置最大等待时间,超时后强制关闭
常见应用场景
- 用户取消:响应用户的取消操作(如取消按钮)
- 超时处理:为长时间运行的操作设置超时
- 资源限制:在系统资源不足时取消低优先级操作
- 应用关闭:应用程序关闭时优雅地停止所有后台任务
性能和资源考虑
- 取消令牌是轻量级的:可以安全地复制和传递
- 及时释放资源:使用 using 语句管理 CancellationTokenSource
- 避免过度检查:在性能敏感的循环中平衡检查频率
- 回调注册管理:及时 Dispose 不再需要的回调注册