简体   繁体   English

不等待所有任务是否安全

[英]Is it safe not to wait for all tasks

Say I have the following action method: 假设我有以下操作方法:

[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");
}

Is Task.WaitAll(tasks.ToArray()) necessary to ensure that all tasks will finish successfully? Task.WaitAll(tasks.ToArray())必须确保所有任务都能成功完成? Will the tasks whose Result happened not to get accessed by the Exists finish their execution in the background successfully? Exists 无法访问其Result的任务是否会成功完成后台执行? Or is there a chance that some of the tasks (that weren't waited for) get dropped since they would not be attached to the request? 或者有些任务(没有等待)会被删除,因为它们不会附加到请求中吗? Is there a better implementation I'm missing? 有没有更好的实施我错过了?

Yes, the tasks are not guaranteed to complete unless something waits for them (with something like an await) 是的,除非等待它们(等待等待的东西),否则不保证任务完成

In your case, the main change you should make is making the Task.WaitAll 在您的情况下,您应该进行的主要更改是创建Task.WaitAll

await Task.WhenAll(tasks);

So it is actually asynchronous. 所以它实际上是异步的。 If you just want to wait for a task to return, use WhenAny instead. 如果你只是想等待一个任务返回,使用WhenAny代替。

Under your provided implementation, the Task.WaitAll call blocks the calling thread until all tasks have completed. 在您提供的实现下, Task.WaitAll调用阻止调用线程,直到所有任务都完成。 It would only proceed to the next line and perform the Exists check after this has happened. 它只会进入下一行,并在发生这种情况后执行Exists检查。 If you remove the Task.WaitAll , then the Exists check would cause the calling thread to block on each task in order; 如果删除Task.WaitAll ,则Exists检查将导致调用线程按顺序阻塞每个任务; ie it first blocks on tasks[0] ; 即它首先阻止tasks[0] ; if this returns false, then it would block on tasks[1] , then tasks[2] , and so on. 如果返回false,则会阻塞tasks[1] ,然后阻塞tasks[2] ,依此类推。 This is not desirable since it doesn't allow for your method to finish early if the tasks complete out of order. 这是不可取的,因为如果任务完成无序,它不允许您的方法提前完成。

If you only need to wait until whichever task returns true first, then you could use Task.WhenAny . 如果您只需要等到任何一个任务首先返回true,那么您可以使用Task.WhenAny This will make your asynchronous method resume as soon as any task completes. 这将使任何任务完成后您的异步方法恢复。 You can then check whether it evaluated to true and return success immediately; 然后,您可以检查它是否评估为true并立即返回成功; otherwise, you keep repeating the process for the remaining collection of tasks until there are none left. 否则,您将继续重复剩余任务集的过程,直到没有剩余任务为止。

If your code was running as an application (WPF, WinForms, Console), then the remaining tasks would continue running on the thread pool until completion, unless the application is shut down. 如果您的代码作为应用程序(WPF,WinForms,Console)运行,那么剩余的任务将继续在线程池上运行直到完成,除非应用程序关闭。 Thread-pool threads are background threads, so they won't keep the process alive if all foreground threads have terminated (eg because all windows were closed). 线程池线程是后台线程,因此如果所有前台线程都已终止(例如因为所有窗口都已关闭),它们将不会使进程保持活动状态。

Since you're running a web app, you incur the risk of having your app pool recycled before the tasks have completed. 由于您运行的是Web应用程序,因此您可能会在任务完成之前回收应用程序池。 The unawaited tasks are fire-and-forget and therefore untracked by the runtime. 未完成的任务是即发即弃的,因此运行时未被跟踪。 To prevent this from happening, you can register them with the runtime through the HostingEnvironment.QueueBackgroundWorkItem method, as suggested in the comments. 为了防止这种情况发生,您可以通过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");
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM