我想在设置超时时触发几项任务。我们的想法是收集击败时钟的任务的结果,并取消(甚至忽略)其他任务。
我尝试使用扩展方法WithCancellation,如here所述,但抛出异常导致WhenAll返回并且没有提供任何结果。
这是我尝试过的,但我也向其他方向开放(注意我需要使用await而不是Task.Run,因为我需要在Tasks中使用httpContext):
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
修改 按照@ Servy的回答,这是我采用的实现:
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));
答案 0 :(得分:1)
如果任何任务未能完成,那么WhenAll
不会返回任何 完成的结果是正确的,它只包含所有失败的聚合异常。幸运的是,您拥有原始的任务集合,因此您可以从那里获得成功完成的结果。
var completedTasks = excutedTasks.Where(t => t.Status == TaskStatus.RanToCompletion);
只需使用它而不是res
。
答案 1 :(得分:0)
我试过你的代码并且它运行得很好,除了取消的任务不处于Faulted状态,而是处于Canceled状态。因此,如果您要处理已取消的任务,请改用t.IsCanceled
。未取消的任务已完成。这是我使用的代码:
public static async Task MainAsync()
{
var urls = new List<string> {"url1", "url2", "url3", "url4", "url5", "url6"};
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
}
}
}
}
public static async Task<MyResults> taskAsync(string url)
{
Console.WriteLine("Start " + url);
var random = new Random();
var delay = random.Next(10);
await Task.Delay(TimeSpan.FromSeconds(delay));
Console.WriteLine("End " + url);
return new MyResults();
}
private static void Main(string[] args)
{
MainAsync().Wait();
}