如何启动一堆后台线程并等待第一个线程返回结果?
在 Google 之后,我尝试了这个方法以及其他十几种方法,包括不使用 lambda。
Task task = Task.Factory.StartNew(async () =>
{
AnalyseInfo analyseInfo = await myAnalyzer.AnalyseAsync();
_analyseInfos.Add(analyseInfo);
});
Task<AnalyseInfo> task = Task<AnalyseInfo>.Factory.StartNew(async () =>
{
return await myAnalyzer.AnalyseAsync();
});
Task<Task<AnalyseInfo>> task = Task<Task<AnalyseInfo>>.Factory.StartNew(async () =>
{
return await myAnalyzer.AnalyseAsync();
});
但没能得到想要的结果。
我想使用等待任何完成的任务Task.WaitAny
// Дождаться завершения любой задачи
int numTask = Task.WaitAny(_tasks.ToArray());
所有尝试都以任务在完成之前获得完成状态而结束。
Task.Factory.StartNew
创建一个包装返回值的新任务,而不管该值本身如何。因此,当您编写 时
Task.Factory.StartNew(async …)
,您会得到Task<Task<…>>
- 一个任务,其价值也是一个任务。更详细地考虑异步方法:
如果调用,它将在第一个非空闲的await运算符之后返回,即第1行和对Bar的调用将同步执行,而等待Bar和第2行将异步执行。当您将此类方法包装在任务中时,第 1 行和对 Bar 的调用将在外部任务中执行,而等待 Bar 和第 2 行将在内部任务中执行。
这就是为什么您最终会过早完成任务:您正在等待外部任务。
如何摆脱它?非常简单:使用 Unwrap!
这个方法没有什么神奇之处,如果它不存在的话 - 自己编写它并不难:
第二个选项 - 您可以使用
Task.Run
,与 StartNew 不同,它对调用内部 Unwrap 的异步方法有一个特殊的重载:最后,第三个选项是根本不将异步方法移至单独的线程,而只是调用它。此选项并不通用,但通常有效:
虽然上面已经给出了答案,但我会尝试用这个例子添加我的 5 美分:
在此示例中,TaskFactory 不用于创建,而是直接使用您的任务(这也消除了额外的异步以及为此任务创建额外的状态机)。在您的示例中,问题在实际等待的任务
await
之前缺少关键字。Task.WhenAny