C# 異步編程與列表任務取消詳解
作者:iamrick
在實際開發中,我們經常需要處理耗時的異步操作,比如網絡請求、文件讀寫等。有時候,我們可能需要取消這些正在進行的異步操作。本文將詳細介紹如何在C#中實現異步操作的取消機制。
在實際開發中,我們經常需要處理耗時的異步操作,比如網絡請求、文件讀寫等。有時候,我們可能需要取消這些正在進行的異步操作。本文將詳細介紹如何在C#中實現異步操作的取消機制。
前置條件
- .NET 5.0或更高版本
- Visual Studio或Visual Studio Code
- 基本的C#異步編程知識
核心概念
在開始之前,讓我們了解幾個重要的概念:
- CancellationTokenSource用于發出取消信號的源
- CancellationToken用于接收取消信號的令牌
- Task表示異步操作的對象
完整示例代碼
下面是一個完整的示例,展示如何實現可取消的異步操作:
using System.Diagnostics;
namespace AppCancellationToken
{
internal class Program
{
// 創建取消令牌源
static readonly CancellationTokenSource s_cts = new CancellationTokenSource();
// 創建HttpClient實例
static readonly HttpClient s_client = new HttpClient
{
MaxResponseContentBufferSize = 1_000_000
};
// 待下載的URL列表
static readonly IEnumerable<string> s_urlList = newstring[]
{
"https://learn.microsoft.com",
"https://learn.microsoft.com/dotnet",
"https://learn.microsoft.com/azure",
"https://learn.microsoft.com/visualstudio"
};
static async Task Main()
{
Console.WriteLine("程序啟動...");
Console.WriteLine("按回車鍵取消下載...\n");
// 創建監聽取消的任務
Task cancelTask = Task.Run(() =>
{
while (Console.ReadKey().Key != ConsoleKey.Enter)
{
Console.WriteLine("按回車鍵取消下載...");
}
Console.WriteLine("\n檢測到回車鍵:正在取消下載...\n");
s_cts.Cancel();
});
// 創建下載任務
Task sumPageSizesTask = SumPageSizesAsync();
// 等待任意一個任務完成
Task finishedTask = await Task.WhenAny(cancelTask, sumPageSizesTask);
if (finishedTask == cancelTask)
{
try
{
await sumPageSizesTask;
Console.WriteLine("在處理取消請求之前下載任務已完成。");
}
catch (OperationCanceledException)
{
Console.WriteLine("下載任務已被取消。");
}
}
Console.WriteLine("程序結束。");
}
static async Task SumPageSizesAsync()
{
var stopwatch = Stopwatch.StartNew();
int total = 0;
foreach (string url in s_urlList)
{
int contentLength = await ProcessUrlAsync(url, s_client, s_cts.Token);
total += contentLength;
}
stopwatch.Stop();
Console.WriteLine($"\n總計下載字節數: {total:#,#}");
Console.WriteLine($"耗時: {stopwatch.Elapsed}\n");
}
static async Task<int> ProcessUrlAsync(string url, HttpClient client, CancellationToken token)
{
HttpResponseMessage response = await client.GetAsync(url, token);
byte[] content = await response.Content.ReadAsByteArrayAsync(token);
Console.WriteLine($"{url,-60} {content.Length,10:#,#}");
return content.Length;
}
}
}
代碼詳解
初始化設置
static readonly CancellationTokenSource s_cts = new CancellationTokenSource();
static readonly HttpClient s_client = new HttpClient { MaxResponseContentBufferSize = 1_000_000 };
- 創建CancellationTokenSource實例用于發出取消信號
- 創建HttpClient實例用于發送HTTP請求
- 使用static readonly確保這些實例在整個應用程序生命周期內只創建一次
主方法實現
主方法使用async Task Main()實現異步入口點,包含兩個主要任務:
- 取消監聽任務(cancelTask)
- 下載處理任務(sumPageSizesTask)
異步下載實現
ProcessUrlAsync方法實現了單個URL的下載邏輯:
static async Task<int> ProcessUrlAsync(string url, HttpClient client, CancellationToken token)
{
HttpResponseMessage response = await client.GetAsync(url, token);
byte[] content = await response.Content.ReadAsByteArrayAsync(token);
Console.WriteLine($"{url,-60} {content.Length,10:#,#}");
return content.Length;
}
- 使用GetAsync方法發送HTTP請求
- 傳入CancellationToken支持取消操作
- 返回下載內容的字節數
運行效果
程序運行后會顯示如下輸出:
圖片
注意
- 始終使用using語句或字段初始化方式創建CancellationTokenSource
- 在所有可取消的異步操作中傳遞CancellationToken
- 正確處理取消異常
- 使用static readonly創建長期使用的HTTP客戶端實例
總結
這種模式適用于需要支持用戶取消的長時間運行的異步操作,如網絡請求、文件下載等場景。
責任編輯:武曉燕
來源:
技術老小子