如何-具有超时和取消的多个异步任务

本文关键字:异步 任务 取消 -具 超时 如何 | 更新日期: 2023-09-27 18:21:05

我想在设置超时的同时激发几个任务。这个想法是从争分夺秒的任务中收集结果,并取消(甚至忽略)其他任务。

我尝试使用这里解释的WithCancellation扩展方法,但是抛出异常导致WhenAll返回并且没有提供结果。

以下是我尝试过的,但我也对其他方向持开放态度(但请注意,我需要使用wait而不是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));

如何-具有超时和取消的多个异步任务

如果任何任务未能完成,那么WhenAll不会返回已完成的任何任务的结果,它只是包装所有失败的聚合异常。幸运的是,您有原始的任务集合,因此您可以从中获得成功完成的结果。

var completedTasks = excutedTasks.Where(t => t.Status == TaskStatus.RanToCompletion);

只需使用它而不是res

我试过你的代码,它运行得很好,只是取消的任务不是处于Faulted状态,而是处于cancelled状态。因此,如果要处理已取消的任务,请改用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();
    }