Cancelar uma lista de tarefas (C#)Cancel a list of tasks (C#)

Você pode cancelar um aplicativo de console assíncrono se não quiser esperar que ele seja concluído.You can cancel an async console application if you don't want to wait for it to finish. Seguindo o exemplo neste tópico, você pode adicionar um cancelamento a um aplicativo que baixa o conteúdo de uma lista de sites.By following the example in this topic, you can add a cancellation to an application that downloads the contents of a list of websites. Você pode cancelar várias tarefas associando a CancellationTokenSource instância a cada tarefa.You can cancel many tasks by associating the CancellationTokenSource instance with each task. Se você selecionar a tecla Enter , cancelará todas as tarefas que ainda não foram concluídas.If you select the Enter key, you cancel all tasks that aren't yet complete.

Este tutorial abrange:This tutorial covers:

  • Criando um aplicativo de console .NETCreating a .NET console application
  • Escrevendo um aplicativo assíncrono que dá suporte ao cancelamentoWriting an async application that supports cancellation
  • Demonstrando o cancelamento de sinalizaçãoDemonstrating signaling cancellation

Pré-requisitosPrerequisites

Este tutorial exige o seguinte:This tutorial requires the following:

Criar aplicativo de exemploCreate example application

Crie um novo aplicativo de console .NET Core.Create a new .NET Core console application. Você pode criar um usando o dotnet new console comando ou do Visual Studio.You can create one by using the dotnet new console command or from Visual Studio. Abra o arquivo Program.cs em seu editor de código favorito.Open the Program.cs file in your favorite code editor.

Substituir usando instruçõesReplace using statements

Substitua as instruções using existentes por estas declarações:Replace the existing using statements with these declarations:

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

Adicionar camposAdd fields

Na Program definição de classe, adicione estes três campos:In the Program class definition, add these three fields:

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

O CancellationTokenSource é usado para sinalizar um cancelamento solicitado para um CancellationToken .The CancellationTokenSource is used to signal a requested cancellation to a CancellationToken. O HttpClient expõe a capacidade de enviar solicitações HTTP e receber respostas http.The HttpClient exposes the ability to send HTTP requests and receive HTTP responses. O s_urlList mantém todas as URLs que o aplicativo planeja processar.The s_urlList holds all of the URLs that the application plans to process.

Atualizar ponto de entrada do aplicativoUpdate application entry point

O ponto de entrada principal no aplicativo de console é o Main método.The main entry point into the console application is the Main method. Substitua o método existente pelo seguinte:Replace the existing method with the following:

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();

    await Task.WhenAny(new[] { cancelTask, sumPageSizesTask });

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

O Main método atualizado agora é considerado um Async Main, que permite um ponto de entrada assíncrono no executável.The updated Main method is now considered an Async main, which allows for an asynchronous entry point into the executable. Ele grava algumas mensagens instrutivas no console e, em seguida, declara uma Task instância chamada cancelTask , que lerá os traços de tecla do console.It writes a few instructional messages to the console, then declares a Task instance named cancelTask, which will read console key strokes. Se a tecla Enter for pressionada, será feita uma chamada para CancellationTokenSource.Cancel() .If the Enter key is pressed, a call to CancellationTokenSource.Cancel() is made. Isso sinalizará o cancelamento.This will signal cancellation. Em seguida, a sumPageSizesTask variável é atribuída a partir do SumPageSizesAsync método.Next, the sumPageSizesTask variable is assigned from the SumPageSizesAsync method. Em seguida, as duas tarefas são passadas para Task.WhenAny(Task[]) o, o que continuará quando qualquer uma das duas tarefas for concluída.Both tasks are then passed to Task.WhenAny(Task[]), which will continue when any of the two tasks have completed.

Criar o método de tamanhos de página de soma assíncronaCreate the asynchronous sum page sizes method

Abaixo do Main método, adicione o SumPageSizesAsync método:Below the Main method, add the SumPageSizesAsync method:

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

O método começa instanciando e iniciando um Stopwatch .The method starts by instantiating and starting a Stopwatch. Em seguida, ele executa um loop em cada URL nas s_urlList chamadas e ProcessUrlAsync .It then loops through each URL in the s_urlList and calls ProcessUrlAsync. Com cada iteração, o s_cts.Token é passado para o ProcessUrlAsync método e o código retorna um Task<TResult> , em que TResult é um inteiro:With each iteration, the s_cts.Token is passed into the ProcessUrlAsync method and the code returns a Task<TResult>, where TResult is an integer:

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

Adicionar método de processoAdd process method

Adicione o seguinte ProcessUrlAsync método abaixo do SumPageSizesAsync método:Add the following ProcessUrlAsync method below the SumPageSizesAsync method:

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

Para qualquer URL fornecida, o método usará a client instância fornecida para obter a resposta como um byte[] .For any given URL, the method will use the client instance provided to get the response as a byte[]. A CancellationToken instância é passada para os HttpClient.GetAsync(String, CancellationToken) HttpContent.ReadAsByteArrayAsync() métodos e.The CancellationToken instance is passed into the HttpClient.GetAsync(String, CancellationToken) and HttpContent.ReadAsByteArrayAsync() methods. O token é usado para se registrar para o cancelamento solicitado.The token is used to register for requested cancellation. O comprimento é retornado depois que a URL e o comprimento são gravados no console.The length is returned after the URL and length is written to the console.

Exemplo de saída de aplicativoExample application output

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

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

ENTER key pressed: cancelling downloads.

Application ending.

Exemplo completoComplete example

O código a seguir é o texto completo do arquivo Program.cs para o exemplo.The following code is the complete text of the Program.cs file for the example.

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

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

    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();

        await Task.WhenAny(new[] { cancelTask, sumPageSizesTask });

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

Consulte tambémSee also

Próximas etapasNext steps