说我有以下操作方法:
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
任务的任务是否可以成功在后台完成其执行?还是由于未将某些任务(未等待的任务)附加到请求中而使其丢失?有没有更好的实现方式?
答案 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");
}