C# 異步編程與列表任務(wù)取消詳解
在實(shí)際開發(fā)中,我們經(jīng)常需要處理耗時的異步操作,比如網(wǎng)絡(luò)請求、文件讀寫等。有時候,我們可能需要取消這些正在進(jìn)行的異步操作。本文將詳細(xì)介紹如何在C#中實(shí)現(xiàn)異步操作的取消機(jī)制。
前置條件
- .NET 5.0或更高版本
 - Visual Studio或Visual Studio Code
 - 基本的C#異步編程知識
 
核心概念
在開始之前,讓我們了解幾個重要的概念:
- CancellationTokenSource用于發(fā)出取消信號的源
 - CancellationToken用于接收取消信號的令牌
 - Task表示異步操作的對象
 
完整示例代碼
下面是一個完整的示例,展示如何實(shí)現(xiàn)可取消的異步操作:
using System.Diagnostics;
namespace AppCancellationToken
{
    internal class Program
    {
        // 創(chuàng)建取消令牌源
        static readonly CancellationTokenSource s_cts = new CancellationTokenSource();
        // 創(chuàng)建HttpClient實(shí)例
        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");
            // 創(chuàng)建監(jiān)聽取消的任務(wù)
            Task cancelTask = Task.Run(() =>
            {
                while (Console.ReadKey().Key != ConsoleKey.Enter)
                {
                    Console.WriteLine("按回車鍵取消下載...");
                }
                Console.WriteLine("\n檢測到回車鍵:正在取消下載...\n");
                s_cts.Cancel();
            });
            // 創(chuàng)建下載任務(wù)
            Task sumPageSizesTask = SumPageSizesAsync();
            // 等待任意一個任務(wù)完成
            Task finishedTask = await Task.WhenAny(cancelTask, sumPageSizesTask);
            if (finishedTask == cancelTask)
            {
                try
                {
                    await sumPageSizesTask;
                    Console.WriteLine("在處理取消請求之前下載任務(wù)已完成。");
                }
                catch (OperationCanceledException)
                {
                    Console.WriteLine("下載任務(wù)已被取消。");
                }
            }
            Console.WriteLine("程序結(jié)束。");
        }
        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總計下載字節(jié)數(shù): {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;
        }
    }
}代碼詳解
初始化設(shè)置
static readonly CancellationTokenSource s_cts = new CancellationTokenSource();
static readonly HttpClient s_client = new HttpClient { MaxResponseContentBufferSize = 1_000_000 };- 創(chuàng)建CancellationTokenSource實(shí)例用于發(fā)出取消信號
 - 創(chuàng)建HttpClient實(shí)例用于發(fā)送HTTP請求
 - 使用static readonly確保這些實(shí)例在整個應(yīng)用程序生命周期內(nèi)只創(chuàng)建一次
 
主方法實(shí)現(xiàn)
主方法使用async Task Main()實(shí)現(xiàn)異步入口點(diǎn),包含兩個主要任務(wù):
- 取消監(jiān)聽任務(wù)(cancelTask)
 - 下載處理任務(wù)(sumPageSizesTask)
 
異步下載實(shí)現(xiàn)
ProcessUrlAsync方法實(shí)現(xiàn)了單個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方法發(fā)送HTTP請求
 - 傳入CancellationToken支持取消操作
 - 返回下載內(nèi)容的字節(jié)數(shù)
 
運(yùn)行效果
程序運(yùn)行后會顯示如下輸出:
圖片
注意
- 始終使用using語句或字段初始化方式創(chuàng)建CancellationTokenSource
 - 在所有可取消的異步操作中傳遞CancellationToken
 - 正確處理取消異常
 - 使用static readonly創(chuàng)建長期使用的HTTP客戶端實(shí)例
 
總結(jié)
這種模式適用于需要支持用戶取消的長時間運(yùn)行的異步操作,如網(wǎng)絡(luò)請求、文件下載等場景。















 
 
 





 
 
 
 