Dela via


Avbryt en lista över uppgifter

Du kan avbryta ett asynkront konsolprogram om du inte vill vänta tills det har slutförts. Genom att följa exemplet i det här avsnittet kan du lägga till en annullering till ett program som laddar ned innehållet i en lista över webbplatser. Du kan avbryta många aktiviteter genom att associera CancellationTokenSource-instansen med varje uppgift. Om du väljer nyckeln Ange avbryter du alla aktiviteter som ännu inte har slutförts.

Den här självstudiekursen omfattar:

  • Skapa ett .NET-konsolprogram
  • Skriva ett asynkront program som stöder annullering
  • Demonstrerar avbokning av signaler

Förutsättningar

Skapa exempelprogram

Skapa ett nytt .NET Core-konsolprogram. Du kan skapa en med hjälp av kommandot dotnet new console eller från Visual Studio. Öppna filen Program.cs i din favoritkodredigerare.

Ersätt med direktiv

Ersätt de befintliga using direktiven med följande deklarationer:

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

Lägg till fält

Lägg till följande tre fält i Program-klassdefinitionen:

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://free.blessedness.top",
    "https://free.blessedness.top/aspnet/core",
    "https://free.blessedness.top/azure",
    "https://free.blessedness.top/azure/devops",
    "https://free.blessedness.top/dotnet",
    "https://free.blessedness.top/dynamics365",
    "https://free.blessedness.top/education",
    "https://free.blessedness.top/enterprise-mobility-security",
    "https://free.blessedness.top/gaming",
    "https://free.blessedness.top/graph",
    "https://free.blessedness.top/microsoft-365",
    "https://free.blessedness.top/office",
    "https://free.blessedness.top/powershell",
    "https://free.blessedness.top/sql",
    "https://free.blessedness.top/surface",
    "https://free.blessedness.top/system-center",
    "https://free.blessedness.top/visualstudio",
    "https://free.blessedness.top/windows",
    "https://free.blessedness.top/maui"
};

CancellationTokenSource används för att signalera en begärd annullering till en CancellationToken. HttpClient gör det möjligt att skicka HTTP-begäranden och ta emot HTTP-svar. s_urlList innehåller alla URL:er som programmet planerar att bearbeta.

Uppdatera programmets startpunkt

Den viktigaste startpunkten i konsolprogrammet är metoden Main. Ersätt den befintliga metoden med följande:

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

Den uppdaterade metoden Main anses nu vara en Async main, vilket möjliggör en asynkron ingångspunkt i den körbara filen. Den skriver några instruktionsmeddelanden till konsolen och deklarerar sedan en Task instans med namnet cancelTask, som läser konsolnyckeldrag. Om Retur tangenten trycks in görs ett anrop till CancellationTokenSource.Cancel(). Detta kommer att signalera annullering. Därefter tilldelas variabeln sumPageSizesTask från metoden SumPageSizesAsync. Båda aktiviteterna skickas sedan till Task.WhenAny(Task[]), som fortsätter när någon av de två aktiviteterna har slutförts.

Nästa kodblock ser till att programmet inte avslutas förrän annulleringen har bearbetats. Om den första uppgiften att slutföra är cancelTask, så väntar man på sumPageSizeTask. Om den avbröts när den väntades kastar den en System.Threading.Tasks.TaskCanceledException. Blocket fångar det undantaget och skriver ut ett meddelande.

Skapa metoden för asynkrona sum-sidstorlekar

Lägg till metoden SumPageSizesAsync under metoden Main:

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

Metoden börjar med att instansiera och starta en Stopwatch. Den loopar sedan igenom varje URL i s_urlList och anropar ProcessUrlAsync. Med varje iteration skickas s_cts.Token till metoden ProcessUrlAsync och koden returnerar en Task<TResult>, där TResult är ett heltal:

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

Lägg till processmetod

Lägg till följande ProcessUrlAsync metod under metoden 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;
}

För en viss URL använder metoden den client instans som tillhandahålls för att hämta svaret som en byte[]. Den CancellationToken instansen skickas till metoderna HttpClient.GetAsync(String, CancellationToken) och HttpContent.ReadAsByteArrayAsync(). token används för att registrera en begäran om annullering. Längden returneras när URL:en och längden har skrivits till konsolen.

Exempel på programutdata

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

https://free.blessedness.top                                       37,357
https://free.blessedness.top/aspnet/core                           85,589
https://free.blessedness.top/azure                                398,939
https://free.blessedness.top/azure/devops                          73,663
https://free.blessedness.top/dotnet                                67,452
https://free.blessedness.top/dynamics365                           48,582
https://free.blessedness.top/education                             22,924

ENTER key pressed: cancelling downloads.

Application ending.

Fullständigt exempel

Följande kod är den fullständiga texten i Program.cs-filen för exemplet.

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://free.blessedness.top",
            "https://free.blessedness.top/aspnet/core",
            "https://free.blessedness.top/azure",
            "https://free.blessedness.top/azure/devops",
            "https://free.blessedness.top/dotnet",
            "https://free.blessedness.top/dynamics365",
            "https://free.blessedness.top/education",
            "https://free.blessedness.top/enterprise-mobility-security",
            "https://free.blessedness.top/gaming",
            "https://free.blessedness.top/graph",
            "https://free.blessedness.top/microsoft-365",
            "https://free.blessedness.top/office",
            "https://free.blessedness.top/powershell",
            "https://free.blessedness.top/sql",
            "https://free.blessedness.top/surface",
            "https://free.blessedness.top/system-center",
            "https://free.blessedness.top/visualstudio",
            "https://free.blessedness.top/windows",
            "https://free.blessedness.top/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;
    }
}

Se även

Nästa steg