Annullare attività asincrone dopo un periodo di tempo

È possibile annullare un'operazione asincrona dopo un periodo di tempo tramite il metodo CancellationTokenSource.CancelAfter se non si vuole attendere fino al completamento dell'operazione. Questo metodo pianifica l'annullamento di qualsiasi attività associata che non è stata completata nel periodo di tempo designato dall'espressione CancelAfter.

Questo esempio viene aggiunto al codice sviluppato in Annullare un elenco di attività (C#) per scaricare un elenco di siti Web e per visualizzare la lunghezza del contenuto di ogni sito.

Contenuto dell'esercitazione:

  • Aggiornamento di un'applicazione console .NET esistente
  • Pianificazione di un annullamento

Prerequisiti

Per completare questa esercitazione, è necessario disporre di:

Aggiornare il punto di ingresso dell'applicazione

Sostituire il metodo Main esistente con quanto segue:

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.");
}

Il metodo aggiornato Main scrive alcuni messaggi di istruzioni nella console. All'interno di try-catch, una chiamata a CancellationTokenSource.CancelAfter(Int32) pianifica un annullamento. Questo segnalerà l'annullamento dopo un periodo di tempo.

Successivamente, resta in attesa il metodo SumPageSizesAsync. Se l'elaborazione di tutti gli URL si verifica più velocemente dell'annullamento pianificato, l'applicazione termina. Tuttavia, se l'annullamento pianificato viene attivato prima dell'elaborazione di tutti gli URL, viene generata un'eccezione OperationCanceledException.

Output dell'applicazione di esempio

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.

Esempio completo

Il codice seguente è il testo completo del file Program.cs per l'esempio.

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/maui"
    };

    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;
    }
}

Vedi anche