I would like to fire several tasks while setting a timeout on them. The idea is to gather the results from the tasks that beat the clock, and cancel (or even just ignore) the other tasks.
I tried using extension methods WithCancellation as explained here, however throwing an exception caused WhenAll to return and supply no results.
Here's what I tried, but I'm opened to other directions as well (note however that I need to use await rather than Task.Run since I need the httpContext in the Tasks):
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3));
IEnumerable<Task<MyResults>> tasks =
from url in urls
select taskAsync(url).WithCancellation(cts.Token);
Task<MyResults>[] excutedTasks = null;
MyResults[] res = null;
try
{
// Execute the query and start the searches:
excutedTasks = tasks.ToArray();
res = await Task.WhenAll(excutedTasks);
}
catch (Exception exc)
{
if (excutedTasks != null)
{
foreach (Task<MyResults> faulted in excutedTasks.Where(t => t.IsFaulted))
{
// work with faulted and faulted.Exception
}
}
}
// work with res
EDIT: Following @Servy's answer below, this is the implementation I went with:
var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3));
IEnumerable<Task<MyResults>> tasks =
from url in urls
select taskAsync(url).WithCancellation(cts.Token);
// Execute the query and start the searches:
Task<MyResults>[] excutedTasks = tasks.ToArray();
try
{
await Task.WhenAll(excutedTasks);
}
catch (OperationCanceledException)
{
// Do nothing - we expect this if a timeout has occurred
}
IEnumerable<Task<MyResults>> completedTasks = excutedTasks.Where(t => t.Status == TaskStatus.RanToCompletion);
var results = new List<MyResults>();
completedTasks.ForEach(async t => results.Add(await t));