Udostępnij za pośrednictwem


Anulowanie listy zadań

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

Ten samouczek obejmuje:

  • Tworzenie aplikacji konsolowej platformy .NET
  • Pisanie aplikacji asynchronicznej obsługującej możliwość anulowania
  • Demonstrowanie anulowania sygnału

Wymagania wstępne

  • Najnowszy .NET SDK
  • Edytor programu Visual Studio Code
  • Zestaw deweloperski C#

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.

Zamień dyrektywy using

Zastąp istniejące using dyrektywy 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 dla CancellationToken. Funkcja HttpClient uwidacznia możliwość wysyłania żądań HTTP i odbierania odpowiedzi HTTP. s_urlList zawiera 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 Task o nazwie cancelTask, które odczytuje naciśnięcia klawiszy konsoli. Jeśli naciśnięty zostanie klawisz Enter, zostanie wykonane wywołanie do CancellationTokenSource.Cancel(). Zasygnalizuje to anulowanie. Następnie zmiennej sumPageSizesTask przypisuje się wartość z metody SumPageSizesAsync. Oba zadania są następnie przekazywane do Task.WhenAny(Task[]), który będzie kontynuował po zakończeniu któregokolwiek z tych dwóch zadań.

Następny blok kodu gwarantuje, że aplikacja nie zostanie zamknięta, dopóki anulowanie nie zostanie przetworzone. Jeśli pierwszym zadaniem do ukończenia jest cancelTask, oczekiwany jest sumPageSizeTask. Jeśli zostało anulowane, podczas oczekiwania rzuca 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 Stopwatch. Następnie iteruje przez każdy adres URL w s_urlList i wywołuje ProcessUrlAsync. W przypadku każdej iteracji element s_cts.Token jest przekazywany do ProcessUrlAsync metody, a kod zwraca Task<TResult>, 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ą metodę ProcessUrlAsync poniżej metody 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;
}

W przypadku dowolnego adresu URL metoda użyje podanego client wystąpienia, aby uzyskać odpowiedź w formacie byte[]. Wystąpienie CancellationToken jest przekazywane do metod HttpClient.GetAsync(String, CancellationToken) i HttpContent.ReadAsByteArrayAsync(). Element token służy do rejestracji 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 dla przykładu.

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