Anulowanie listy zadań

Jeśli nie chcesz czekać na zakończenie, możesz anulować aplikację konsolową asynchronicznych. Korzystając z przykładu w tym temacie, możesz dodać anulowanie do aplikacji, która pobiera zawartość listy witryn internetowych. Wiele zadań można anulować, kojarząc CancellationTokenSource wystąpienie z każdym zadaniem. Jeśli wybierzesz klawisz Enter , anulujesz wszystkie zadania, które nie zostały jeszcze ukończone.

W tym samouczku opisano następujące czynności:

  • Tworzenie aplikacji konsolowej platformy .NET
  • Pisanie aplikacji asynchronicznych obsługującej anulowanie
  • Demonstrowanie anulowania sygnału

Wymagania wstępne

Dla tego samouczka wymagane są następujące elementy:

Tworzenie przykładowej aplikacji

Utwórz nową aplikację konsolową platformy .NET Core. Możesz go utworzyć przy użyciu dotnet new console polecenia lub programu Visual Studio. Otwórz plik Program.cs w ulubionym edytorze kodu.

Zastępowanie instrukcji using

Zastąp istniejące instrukcje using następującymi deklaracjami:

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

Dodaj pola

Program W definicji klasy dodaj następujące trzy pola:

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

Element CancellationTokenSource służy do sygnalizowania żądanego anulowania do .CancellationToken Funkcja HttpClient uwidacznia możliwość wysyłania żądań HTTP i odbierania odpowiedzi HTTP. Zawiera s_urlList wszystkie adresy URL, które aplikacja planuje przetworzyć.

Aktualizowanie punktu wejścia aplikacji

Głównym punktem wejścia do aplikacji konsolowej jest Main metoda . Zastąp istniejącą metodę następującym kodem:

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

Zaktualizowana Main metoda jest teraz traktowana jako główna asynchroniczna, która umożliwia asynchroniczny punkt wejścia do pliku wykonywalnego. Zapisuje kilka komunikatów instruktażowych w konsoli, a następnie deklaruje wystąpienie o nazwie cancelTask, które odczytuje Task pociągnięcia klucza konsoli. Jeśli klawisz Enter zostanie naciśnięty, zostanie wykonane wywołanie CancellationTokenSource.Cancel() metody . Spowoduje to anulowanie. Następnie zmienna sumPageSizesTask jest przypisywana SumPageSizesAsync z metody . Oba zadania są następnie przekazywane do Task.WhenAny(Task[])elementu , co będzie kontynuowane po zakończeniu któregokolwiek z dwóch zadań.

Następny blok kodu gwarantuje, że aplikacja nie zostanie zakończona, dopóki anulowanie nie zostanie przetworzone. Jeśli pierwszym zadaniem do ukończenia cancelTaskjest , oczekiwany jest element sumPageSizeTask . Jeśli została anulowana, gdy oczekiwano, zgłasza wartość System.Threading.Tasks.TaskCanceledException. Blok przechwytuje ten wyjątek i wyświetla komunikat.

Tworzenie metody asynchronicznej sumowania rozmiarów stron

Main Poniżej metody dodaj metodę SumPageSizesAsync :

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

Metoda rozpoczyna się od utworzenia wystąpienia i uruchomienia klasy Stopwatch. Następnie przechodzi przez każdy adres URL w s_urlList pętli i wywołuje metodę ProcessUrlAsync. W przypadku każdej iteracji element s_cts.Token jest przekazywany do ProcessUrlAsync metody , a kod zwraca Task<TResult>wartość , gdzie TResult jest liczbą całkowitą:

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

Dodawanie metody procesu

Dodaj następującą ProcessUrlAsync metodę SumPageSizesAsync poniżej metody:

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

W przypadku dowolnego adresu URL metoda użyje podanego client wystąpienia, aby uzyskać odpowiedź jako byte[]. Wystąpienie CancellationToken jest przekazywane do HttpClient.GetAsync(String, CancellationToken) metod i HttpContent.ReadAsByteArrayAsync() . Element token służy do rejestrowania się w celu zażądanego anulowania. Długość jest zwracana po zapisaniu adresu URL i długości do konsoli.

Przykładowe dane wyjściowe aplikacji

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.

Kompletny przykład

Poniższy kod jest kompletnym tekstem pliku Program.cs w tym przykładzie.

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

Zobacz też

Następne kroki