不等待所有任务是否安全?

时间:2019-01-10 21:58:08

标签: c# .net asp.net-core async-await

说我有以下操作方法:

NA

是否需要[HttpPost] public async Task<IActionResult> PostCall() { var tasks = new List<Task<bool>>(); for (int i = 0; i < 10; i++) tasks.Add(Manager.SomeMethodAsync(i)); // Is this line necessary to ensure that all tasks will finish successfully? Task.WaitAll(tasks.ToArray()); if (tasks.Exists(x => x.Result)) return new ObjectResult("At least one task returned true"); else return new ObjectResult("No tasks returned true"); } 来确保所有任务都能成功完成? Task.WaitAll(tasks.ToArray()) 发生了Result任务的任务是否可以成功在后台完成其执行?还是由于未将某些任务(未等待的任务)附加到请求中而使其丢失?有没有更好的实现方式?

2 个答案:

答案 0 :(得分:3)

是的,除非有等待的任务(例如等待),否则不能保证任务会完成

对于您而言,您应该进行的主要更改是进行Task.WaitAll

await Task.WhenAll(tasks);

因此它实际上是异步的。如果您只想等待 a 任务返回,请改用WhenAny

答案 1 :(得分:3)

在您提供的实现中,Task.WaitAll调用会阻塞调用线程,直到所有任务完成。它只会继续到下一行,并在发生这种情况后执行Exists检查。如果删除Task.WaitAll,则Exists检查将导致调用线程按顺序阻塞每个任务;即它首先在tasks[0]上阻止;如果返回false,则它将在tasks[1],然后在tasks[2]上进行阻止,依此类推。这是不可取的,因为如果任务无序完成,它不允许您的方法提早完成。

如果您只需要等到第一个任务返回true即可,则可以使用Task.WhenAny。这将使您的异步方法在 any 任务完成后立即恢复。然后,您可以检查它是否评估为true并立即返回成功;否则,您将对剩余的任务重复该过程,直到没有剩下的为止。

如果您的代码作为应用程序(WPF,WinForms,控制台)运行,则除非关闭应用程序,否则其余任务将继续在线程池上运行直到完成。线程池线程是后台线程,因此如果所有前台线程都已终止(例如,因为所有窗口均已关闭),它们将无法使进程保持活动状态。

由于您正在运行Web应用程序,因此存在在任务完成之前回收应用程序池的风险。即将执行的任务是一劳永逸的,因此运行时不会对其进行跟踪。为了防止这种情况发生,您可以按照注释中的建议通过HostingEnvironment.QueueBackgroundWorkItem方法在运行时中注册它们。

[HttpPost]
public async Task<IActionResult> PostCall()
{
    var tasks = Enumerable
        .Range(0, 10)
        .Select(Manager.SomeMethodAsync)
        .ToList();

    foreach (var task in tasks)
        HostingEnvironment.QueueBackgroundWorkItem(_ => task);

    while (tasks.Any())
    {
        var readyTask = await Task.WhenAny(tasks);
        tasks.Remove(readyTask);
        if (await readyTask)
            return new ObjectResult("At least one task returned true");
    }

    return new ObjectResult("No tasks returned true");
}