Delen via


Een lijst met taken annuleren

U kunt een asynchrone consoletoepassing annuleren als u niet wilt wachten totdat deze is voltooid. Door het voorbeeld in dit onderwerp te volgen, kunt u een annulering toevoegen aan een toepassing waarmee de inhoud van een lijst met websites wordt gedownload. U kunt veel taken annuleren door het CancellationTokenSource exemplaar aan elke taak te koppelen. Als u de Enter-toets selecteert, annuleert u alle taken die nog niet zijn voltooid.

In deze zelfstudie komt het volgende aan bod:

  • Een .NET-consoletoepassing maken
  • Een asynchrone toepassing schrijven die annulering ondersteunt
  • Het demonstreren van het annuleren van signalen.

Benodigdheden

Voorbeeldtoepassing maken

Maak een nieuwe .NET Core-consoletoepassing. U kunt er een maken met behulp van de opdracht dotnet new console of vanuit Visual Studio. Open het bestand Program.cs in uw favoriete code-editor.

Het vervangen van gebruiksrichtlijnen

Vervang de bestaande using-instructies door deze declaraties:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

Velden toevoegen

Voeg in de Program klassedefinitie deze drie velden toe:

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

De CancellationTokenSource wordt gebruikt om een aangevraagde annulering aan te geven aan een CancellationToken. De HttpClient geeft de mogelijkheid om HTTP-aanvragen te verzenden en HTTP-antwoorden te ontvangen. De s_urlList bevat alle URL's die door de toepassing moeten worden verwerkt.

Toepassingsinvoerpunt bijwerken

Het belangrijkste toegangspunt in de consoletoepassing is de Main methode. Vervang de bestaande methode door het volgende:

static async Task Main()
{
    Console.WriteLine("Application started.");
    Console.WriteLine("Press the ENTER key to cancel...\n");

    Task cancelTask = Task.Run(() =>
    {
        while (Console.ReadKey().Key != ConsoleKey.Enter)
        {
            Console.WriteLine("Press the ENTER key to cancel...");
        }

        Console.WriteLine("\nENTER key pressed: cancelling downloads.\n");
        s_cts.Cancel();
    });

    Task sumPageSizesTask = SumPageSizesAsync();

    Task finishedTask = await Task.WhenAny(new[] { cancelTask, sumPageSizesTask });
    if (finishedTask == cancelTask)
    {
        // wait for the cancellation to take place:
        try
        {
            await sumPageSizesTask;
            Console.WriteLine("Download task completed before cancel request was processed.");
        }
        catch (TaskCanceledException)
        {
            Console.WriteLine("Download task has been cancelled.");
        }
    }

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

De bijgewerkte methode Main wordt nu beschouwd als een hoofd-van Async, waarmee een asynchroon invoerpunt in het uitvoerbare bestand mogelijk is. Er worden enkele instructieberichten naar de console geschreven, en vervolgens wordt er een Task-instantie met de naam cancelTaskgedeclareerd, die toetsaanslagen op de console zal lezen. Als de Enter-toets wordt ingedrukt, wordt er een aanroep naar CancellationTokenSource.Cancel() uitgevoerd. Dit geeft annulering aan. Vervolgens wordt de variabele sumPageSizesTask toegewezen vanuit de methode SumPageSizesAsync. Beide taken worden vervolgens doorgegeven aan Task.WhenAny(Task[]), die worden voortgezet wanneer een van de twee taken is voltooid.

Het volgende codeblok zorgt ervoor dat de toepassing pas wordt afgesloten nadat de annulering is verwerkt. Als de eerste taak de cancelTaskis, moet de sumPageSizeTask worden afgewacht. Als het wordt geannuleerd en het wordt afgewacht, werpt het een System.Threading.Tasks.TaskCanceledException. Het blok vangt dat exceptie op en print een bericht.

Maak de methode voor het asynchroon optellen van paginagroottes aan

Voeg onder de methode Main de SumPageSizesAsync toe:

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

De methode begint met het instantiëren en starten van een Stopwatch. Vervolgens wordt elke URL in de s_urlList doorlopen en wordt ProcessUrlAsyncaangeroepen. Bij elke iteratie wordt de s_cts.Token doorgegeven aan de ProcessUrlAsync methode en retourneert de code een Task<TResult>, waarbij TResult een geheel getal is:

int total = 0;
foreach (string url in s_urlList)
{
    int contentLength = await ProcessUrlAsync(url, s_client, s_cts.Token);
    total += contentLength;
}

Procesmethode toevoegen

Voeg de volgende ProcessUrlAsync methode toe onder de methode SumPageSizesAsync:

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

Voor een bepaalde URL gebruikt de methode de client instantie die is opgegeven om het antwoord te verkrijgen als een byte[]. Het CancellationToken-exemplaar wordt doorgegeven aan de HttpClient.GetAsync(String, CancellationToken)- en HttpContent.ReadAsByteArrayAsync()-methoden. De token wordt gebruikt voor het registreren van een aangevraagde annulering. De lengte wordt geretourneerd nadat de URL en lengte naar de console is geschreven.

Voorbeeld van toepassingsuitvoer

Application started.
Press the ENTER key to cancel...

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
https://learn.microsoft.com/dotnet                                67,452
https://learn.microsoft.com/dynamics365                           48,582
https://learn.microsoft.com/education                             22,924

ENTER key pressed: cancelling downloads.

Application ending.

Volledig voorbeeld

De volgende code is de volledige tekst van het Program.cs-bestand voor het voorbeeld.

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.");
        Console.WriteLine("Press the ENTER key to cancel...\n");

        Task cancelTask = Task.Run(() =>
        {
            while (Console.ReadKey().Key != ConsoleKey.Enter)
            {
                Console.WriteLine("Press the ENTER key to cancel...");
            }

            Console.WriteLine("\nENTER key pressed: cancelling downloads.\n");
            s_cts.Cancel();
        });

        Task sumPageSizesTask = SumPageSizesAsync();

        Task finishedTask = await Task.WhenAny(new[] { cancelTask, sumPageSizesTask });
        if (finishedTask == cancelTask)
        {
            // wait for the cancellation to take place:
            try
            {
                await sumPageSizesTask;
                Console.WriteLine("Download task completed before cancel request was processed.");
            }
            catch (OperationCanceledException)
            {
                Console.WriteLine("Download task has been cancelled.");
            }
        }

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

Zie ook

Volgende stappen