一定時間後の非同期タスクのキャンセル

CancellationTokenSource.CancelAfter メソッドを使用すると、一定の時間が過ぎた後に非同期操作が完了するまで待たない場合に、キャンセルすることができます。 このメソッドは、CancelAfter 式によって指定された時間内に完了しない、関連付けられたタスクのキャンセルをスケジュールします。

この例は、タスクの一覧のキャンセル (C#)に関する記事で開発したコードに追加して、Web サイトの一覧をダウンロードしてそれぞれのコンテンツの長さを表示します。

このチュートリアルの内容:

  • 既存の .NET コンソール アプリケーションの更新
  • キャンセルのスケジュール

前提条件

このチュートリアルには、次のものが必要です。

アプリケーション エントリ ポイントの更新

既存のMain メソッドを以下に置き換えます。

static async Task Main()
{
    Console.WriteLine("Application started.");

    try
    {
        s_cts.CancelAfter(3500);

        await SumPageSizesAsync();
    }
    catch (OperationCanceledException)
    {
        Console.WriteLine("\nTasks cancelled: timed out.\n");
    }
    finally
    {
        s_cts.Dispose();
    }

    Console.WriteLine("Application ending.");
}

更新された Main メソッドで、いくつかの指示メッセージがコンソールに書き込まれます。 try-catch 内で、CancellationTokenSource.CancelAfter(Int32) を呼び出してキャンセルをスケジュールします。 これにより、一定の期間後にキャンセルが通知されます。

次に、SumPageSizesAsync メソッドが待機されます。 スケジュールされたキャンセルよりも、すべての URL の処理が早く行われると、アプリケーションは終了します。 ただし、すべての URL が処理される前にスケジュールされたキャンセルがトリガーされると、OperationCanceledException がスローされます。

アプリケーション出力の例

Application started.

https://learn.microsoft.com                                       37,357
https://learn.microsoft.com/aspnet/core                           85,589
https://learn.microsoft.com/azure                                398,939
https://learn.microsoft.com/azure/devops                          73,663

Tasks cancelled: timed out.

Application ending.

コード例全体

次のコードは、この例の Program.cs ファイルの完全なテキストです。

using System.Diagnostics;

class Program
{
    static readonly CancellationTokenSource s_cts = new CancellationTokenSource();

    static readonly HttpClient s_client = new HttpClient
    {
        MaxResponseContentBufferSize = 1_000_000
    };

    static readonly IEnumerable<string> s_urlList = new string[]
    {
            "https://learn.microsoft.com",
            "https://learn.microsoft.com/aspnet/core",
            "https://learn.microsoft.com/azure",
            "https://learn.microsoft.com/azure/devops",
            "https://learn.microsoft.com/dotnet",
            "https://learn.microsoft.com/dynamics365",
            "https://learn.microsoft.com/education",
            "https://learn.microsoft.com/enterprise-mobility-security",
            "https://learn.microsoft.com/gaming",
            "https://learn.microsoft.com/graph",
            "https://learn.microsoft.com/microsoft-365",
            "https://learn.microsoft.com/office",
            "https://learn.microsoft.com/powershell",
            "https://learn.microsoft.com/sql",
            "https://learn.microsoft.com/surface",
            "https://learn.microsoft.com/system-center",
            "https://learn.microsoft.com/visualstudio",
            "https://learn.microsoft.com/windows",
            "https://learn.microsoft.com/xamarin"
    };

    static async Task Main()
    {
        Console.WriteLine("Application started.");

        try
        {
            s_cts.CancelAfter(3500);

            await SumPageSizesAsync();
        }
        catch (OperationCanceledException)
        {
            Console.WriteLine("\nTasks cancelled: timed out.\n");
        }
        finally
        {
            s_cts.Dispose();
        }

        Console.WriteLine("Application ending.");
    }

    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($"\nTotal bytes returned:  {total:#,#}");
        Console.WriteLine($"Elapsed time:          {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;
    }
}

関連項目