HttpClient - Uma tarefa foi cancelada?

191

Funciona bem quando há uma ou duas tarefas, porém gera um erro "Uma tarefa foi cancelada" quando temos mais de uma tarefa listada.

insira a descrição da imagem aqui

List<Task> allTasks = new List<Task>();
allTasks.Add(....);
allTasks.Add(....);
Task.WaitAll(allTasks.ToArray(), configuration.CancellationToken);


private static Task<T> HttpClientSendAsync<T>(string url, object data, HttpMethod method, string contentType, CancellationToken token)
{
    HttpRequestMessage httpRequestMessage = new HttpRequestMessage(method, url);
    HttpClient httpClient = new HttpClient();
    httpClient.Timeout = new TimeSpan(Constants.TimeOut);

    if (data != null)
    {
        byte[] byteArray = Encoding.ASCII.GetBytes(Helper.ToJSON(data));
        MemoryStream memoryStream = new MemoryStream(byteArray);
        httpRequestMessage.Content = new StringContent(new StreamReader(memoryStream).ReadToEnd(), Encoding.UTF8, contentType);
    }

    return httpClient.SendAsync(httpRequestMessage).ContinueWith(task =>
    {
        var response = task.Result;
        return response.Content.ReadAsStringAsync().ContinueWith(stringTask =>
        {
            var json = stringTask.Result;
            return Helper.FromJSON<T>(json);
        });
    }).Unwrap();
}
Karthikeyan Vijayakumar
fonte
O que diz a exceção interna?
RagtimeWilly
1
Por que você pega um CancellationTokenparâmetro as e não o usa?
precisa saber é
1
A razão para me foi a eliminação HttpClientpor engano, por exemploasync Task<HttpResponseMessage> Method(){ using(var client = new HttpClient()) return client.GetAsync(request); }
JobaDiniz
3
Para aqueles que usam HttpClientcomo @JobaDiniz (com a using()), pare! O motivo: aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong
Philippe

Respostas:

272

Existem 2 razões prováveis ​​para que um TaskCanceledExceptionseja lançado:

  1. Algo chamado Cancel()no CancellationTokenSourceassociado ao token de cancelamento antes da conclusão da tarefa.
  2. A solicitação atingiu o tempo limite, ou seja, não foi concluída dentro do período especificado HttpClient.Timeout.

Meu palpite é que foi um tempo limite. (Se fosse um cancelamento explícito, você provavelmente descobriria isso.) Você pode ter mais certeza inspecionando a exceção:

try
{
    var response = task.Result;
}
catch (TaskCanceledException ex)
{
    // Check ex.CancellationToken.IsCancellationRequested here.
    // If false, it's pretty safe to assume it was a timeout.
}
Todd Menier
fonte
3
Então, o que é uma solução possível? Eu tenho um problema semelhante. stackoverflow.com/questions/36937328/…
Desenvolvedor
48
@Dimi - isso é bem antigo, mas a solução que usei foi definir a propriedade Timeout com um valor maior:httpClient.Timeout = TimeSpan.FromMinutes(30)
RQDQ
2
@RQDQ, você é o cara, cara! Não usar o construtor resolveu o problema para mim. No meu caso específico, eu queria um tempo limite em milissegundos. Usando TimeSpan.FromMilliseconds(Configuration.HttpTimeout)em oposição a new TimeSpan(Configuration.HttpTimeout)trabalhou um tratamento. Obrigado!
Victor Ude
6
O @RQDQ httpClient.Timeout = TimeSpan.FromMinutes(30)não é uma boa abordagem, porque bloqueará esse encadeamento específico por 30 minutos e também não atingirá o ponto de extremidade HTTP (que é sua principal tarefa). Além disso, se o seu programa terminar antes de 30 minutos, é mais provável que você encontre ThreadAbortException. Uma abordagem melhor seria descobrir por que esse ponto de extremidade HTTP não está sendo atingido, pode exigir VPN ou algum acesso restrito à rede.
Amit Upadhyay
6
@AmitUpadhyay Se a chamada for feita await, nenhum thread será bloqueado. Não é o encadeamento da interface do usuário, não é um encadeamento de conjunto de encadeamentos, outro encadeamento em segundo plano, nenhum.
Todd Menier
20

Corri para esse problema porque meu Main()método não estava aguardando a conclusão da tarefa antes de retornar, e Task<HttpResponseMessage> myTaskestava sendo cancelado quando meu programa de console foi encerrado.

A solução foi chamar myTask.GetAwaiter().GetResult()em Main()(a partir de esta resposta ).

Ben Hutchison
fonte
9

Outra possibilidade é que o resultado não seja esperado no lado do cliente. Isso pode acontecer se qualquer método na pilha de chamadas não usar a palavra-chave wait para aguardar a conclusão da chamada.

Manish
fonte
7
var clientHttp = new HttpClient();
clientHttp.Timeout = TimeSpan.FromMinutes(30);

A descrição acima é a melhor abordagem para aguardar uma grande solicitação. Você está confuso cerca de 30 minutos; é hora aleatória e você pode dar o tempo que quiser.

Em outras palavras, a solicitação não aguardará 30 minutos se eles obtiverem resultados antes de 30 minutos. 30 min significa que o tempo de processamento da solicitação é de 30 min. Quando ocorreu o erro "A tarefa foi cancelada" ou requisitos de solicitação de dados grandes.

Navdeep Kapil
fonte
0

Outro motivo pode ser o fato de você estar executando o serviço (API) e colocar um ponto de interrupção no serviço (e seu código ficar travado em algum ponto de interrupção (por exemplo, a solução do Visual Studio está mostrando a Depuração em vez de Executando )). e, em seguida, pressionando a API no código do cliente. Portanto, se o código de serviço estiver em pausa em algum ponto de interrupção, basta pressionar F5 no VS.

vivek nuna
fonte
0

Na minha situação, o método do controlador não foi feito como assíncrono e o método chamado dentro do método do controlador era assíncrono.

Então acho que é importante usar async / wait até o nível superior para evitar problemas como esses.

chaitanyasingu
fonte