Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Você pode cancelar um aplicativo de console assíncrono se não quiser esperar que ele seja concluído. Seguindo o exemplo neste tópico, você pode adicionar um cancelamento a um aplicativo que baixa o conteúdo de uma lista de sites. Você pode cancelar várias tarefas associando a instância CancellationTokenSource a cada tarefa. Se você selecionar a tecla Enter, você cancele todas as tarefas que ainda não foram concluídas.
Este tutorial abrange:
- Criando um aplicativo de console .NET
- Escrever um aplicativo assíncrono que dá suporte a cancelamento
- Demonstrando cancelamento de sinalização
Pré-requisitos
Este tutorial exige o seguinte:
- .NET 5 ou SDK posterior
- IDE (ambiente de desenvolvimento integrado)
Criar aplicativo de exemplo
Criar um novo aplicativo de console do .NET Core. Você pode criar um usando o comando ou do dotnet new consoleVisual Studio. Abra o arquivo Program.cs em seu editor de código favorito.
Substituir usando instruções
Substitua as instruções de uso existentes por estas declarações:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
Adicionar campos
Na definição de classe Program, adicione estes três campos:
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"
};
O CancellationTokenSource é usado para sinalizar um cancelamento solicitado para um CancellationToken. O HttpClient expõe a capacidade de enviar solicitações HTTP e receber respostas HTTP. O s_urlList contém todas as URLs que o aplicativo planeja processar.
Atualize o ponto de entrada do aplicativo
O principal ponto de entrada no aplicativo de console é o método Main. Substitua o método existente pelo seguinte:
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.");
}
O método atualizado Main agora é considerado um Async main, que permite um ponto de entrada assíncrono no executável. Ele grava algumas mensagens instrutivas no console e, em seguida, declara uma instância Task chamada cancelTask, que lerá os traços de chave do console. Se a tecla Enter for pressionada, será feita uma chamada CancellationTokenSource.Cancel(). Isso sinalizará o cancelamento. Em seguida, a variável sumPageSizesTask é atribuída do método SumPageSizesAsync. Ambas as tarefas são então passadas para Task.WhenAny(Task[]), o que continuará quando qualquer uma das duas tarefas tiver sido concluída.
O próximo bloco de código garante que o aplicativo não saia até que o cancelamento seja processado. Se a primeira tarefa a ser concluída for cancelTask, o sumPageSizeTask será aguardado. Se ele foi cancelado, quando aguardado, ele lança um System.Threading.Tasks.TaskCanceledException. O bloco captura essa exceção e imprime uma mensagem.
Criar o método de tamanhos de página de soma assíncrona
Abaixo do método Main, adicione o método 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");
}
O método começa instanciando e iniciando um Stopwatch. Em seguida, ele faz loops por cada URL no s_urlList e chama ProcessUrlAsync. A cada iteração, o método s_cts.Token é passado para o método ProcessUrlAsync e o código retorna um Task<TResult>, que que TResult é um inteiro:
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 processo
Adicione o seguinte método ProcessUrlAsync abaixo do método 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;
}
Para qualquer URL fornecida, o método usará a instância client fornecida para obter a resposta como um byte[]. A instância CancellationToken é passada para os métodos HttpClient.GetAsync(String, CancellationToken) e HttpContent.ReadAsByteArrayAsync(). O token é usado para registrar-se para cancelamento solicitado. O comprimento é retornado depois que a URL e o comprimento são gravados no console.
Exemplo de saída de aplicativo
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.
Exemplo completo
O código a seguir é o texto completo do arquivo Program.cs para o exemplo.
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;
}
}