Por que minha tarefa não é cancelada?
Estou executando um processo que periodicamente se curva a uma lista de URLs para testar o desempenho desses sites. Meu programa principal é basicamente um loop do ... sleep ... while que chama uma função, runTest () a cada N segundos, com uma interrupção do teclado para eliminá-lo. Analisado o básico, meu código é o seguinte:
public void runTest()
{
if(isRunning)
{
return; //Plus some logging that one or more of the tests hasn't completed
}
try {
isRunning = true;
Curl.GlobalInit((int)CURLinitFlag.CURL_GLOBAL_ALL);
CancellationTokenSource cts = new CancellationTokenSource(httpTimeoutValueMs * 2);
foreach (var url in urls)
taskList.Add(doAsyncCurls(url, cts))
List<CurlResults> listOfResults = Task.WhenAll(taskList.Select(x => x)).Result.ToList();
taskList.Clear();
}
catch {/*Some exception handling code*/}
finally {
isRunning = false;
Curl.GlobalCleanup();
}
}
private static async Task<CurlResults> doAsyncCurls(string url, CancellationTokenSource cts)
{
try
{
/*Initiate a Curl using libCurl*/
Easy easy = new Easy();
easy.SetOpt(CURLoption.CURLOPT_URL, url);
easy.SetOpt(CURLoption.CURLOPT_DNS_CACHE_TIMEOUT, 0); //Disables DNS cache
easy.SetOpt(CURLoption.CURLOPT_CONNECTTIMEOUT, httpTimeoutValueMs / 1000); //20sec timeout
Task t = new Task(() => easy.Perform(), cts.Token);
t.Start();
await t;
return new CurlResults(/*valid results parameters*/);
}
catch (TaskCanceledException)
{ /*Cancellation token invoked*/
return new CurlResults(/*Invalid results parameters*/);
}
catch (Exception e)
{ /*Other exception handling*/
return new CurlResults(/*Invalid results parameters*/);
}
A função "doAsyncCurl" faz o que diz na lata, configurando um valor de tempo limite http para a metade do token de cancelamento, para que a solicitação HTTP evapore antes que o token de cancelamento seja chamado e gere um resultado negativo. Adicionei o token de cancelamento para tentar resolver o meu problema, conforme abaixo.
Deixo isso em execução há séculos - para começar, tudo funciona bem, mas eventualmente (centenas, se não mais iterações, pela invocação periódica runTest ()) parece que uma das tarefas fica presa e o token de cancelamento não é invocado, e o processo de teste de ondulação está efetivamente preso.
Além de escolher quais URLs são problemáticos (e todos são válidos), o que pode ser feito para diagnosticar o que está errado? Ou construí meus cachos paralelos completamente errados para começar? (Compreendo que eu poderia instanciar novos cachos para URLs que terminaram na rodada anterior e deixar o pendurado pendurado, em vez de esperar que todos terminem antes de iniciar um novo lote, mas não estou preocupado com essa eficiência ganho).