假设我有以下操作方法:
[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
not 未被 Exists
访问的任务是否会在后台成功完成执行?或者是否有可能因为某些任务(未等待)被丢弃,因为它们不会附加到请求中?我是否缺少更好的实现方式?
最佳答案
在您提供的实现下,Task.WaitAll
调用会阻塞调用线程,直到所有 任务完成。它只会继续下一行并在发生这种情况后执行 Exists
检查。如果删除 Task.WaitAll
,则 Exists
检查将导致调用线程按顺序阻塞每个任务;即它首先在 tasks[0]
上阻塞;如果这返回 false,那么它将阻塞 tasks[1]
,然后是 tasks[2]
,依此类推。这是不可取的,因为如果任务无序完成,它不允许您的方法提前完成。
如果您只需要等到哪个任务首先返回 true,那么您可以使用 Task.WhenAny
。这将使您的异步方法在任何 任务完成后立即恢复。然后您可以检查它是否评估为真并立即返回成功;否则,您将继续对剩余的任务集合重复该过程,直到没有任务为止。
如果您的代码作为应用程序(WPF、WinForms、控制台)运行,则其余任务将继续在线程池上运行直到完成,除非应用程序已关闭。线程池线程是后台线程,因此如果所有前台线程都已终止(例如,因为所有窗口都已关闭),它们将不会使进程保持事件状态。
由于您运行的是网络应用程序,因此您会面临在任务完成之前回收应用程序池的风险。未等待的任务是即发即弃的,因此不会被运行时跟踪。为防止这种情况发生,您可以通过 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");
}
关于c# - 不等待所有任务是否安全,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54137513/