如何等待有条件的任务?

我有一些任务返回一个布尔。 我只想等到任何任务首先返回True时。 可能吗 ?

我的第一个想法是使用CancellationTokenSource,但它不是一个好主意,因为它在我调用Task.WaitAll方法时抛出exception。

第二个选项是使用我在参考中传递的bool,如果是,则直接返回。 它有效,但它不是高效的:

bool isFound = false; Task t0 = Task.Factory.StartNew(() => Find(paramA, paramB, ref isFound)); Task t1 = Task.Factory.StartNew(() => Find(paramC, paramD, ref isFound)); Task t2 = Task.Factory.StartNew(() => Find(paramE, paramF, ref isFound)); Task t3 = Task.Factory.StartNew(() => Find(paramG, paramH, ref isFound)); Task.WaitAll(new Task[] { t0, t1, t2, t3, t4 }); return t0.Result | t1.Result | t2.Result | t3.Result | t4.Result; 

并在方法中:

  private static bool Find(int[,] m1, int[,] m2, ref bool isFound) { if (isFound) return false; // Do work... } 

编辑:

作为预先给出的答案,我现在使用TaskCompletionSource

 TaskCompletionSource tcs = new TaskCompletionSource(); Task t0 = Task.Factory.StartNew(() => Find(paramA, paramB); Task t1 = Task.Factory.StartNew(() => Find(paramC, paramD); Task t2 = Task.Factory.StartNew(() => Find(paramE, paramF); Task t3 = Task.Factory.StartNew(() => Find(paramG, paramH); t0.ContinueWith(_ => { if (t0.Result) tcs.TrySetResult(t0.Result); }); t1.ContinueWith(_ => { if (t1.Result) tcs.TrySetResult(t1.Result); }); t2.ContinueWith(_ => { if (t2.Result) tcs.TrySetResult(t2.Result); }); t3.ContinueWith(_ => { if (t3.Result) tcs.TrySetResult(t3.Result); }); t4.ContinueWith(_ => { if (t4.Result) tcs.TrySetResult(t4.Result); }); tcs.Task.Wait(); return tcs.Task.Result; 

在这种情况下,当所有任务返回false时,没有注意到任何事情,这是正常的。 但是我不知道如何使用WhenAll方法。 我试着添加这个:

 tcs.Task.Wait(); Task tr = Task.WhenAll(new Task[] { t0, t1, t2, t3, t4 }); if (tr.IsCompleted) return false; else return tcs.Task.Result; 

但它不起作用:(

一种选择是创建任何类型的TaskCompletionSource (如果需要,则标识结果)。 然后为每个任务添加一个延续,如果结果为true,则调用TaskCompletionSource.TrySetResult

然后等待TaskCompletionSource.Task 。 它避免了您不得不重复调用Task.WaitAny ,检查结果等。

棘手的一点是注意到所有任务都返回错误…在.NET 4.5中,这将相当容易,通过Task.WhenAll创建另一个任务 – 然后你只需要等待{ success, all failed }一个完成。

你需要WaitHandle.WaitAny 。 在一开始你设置了一个WaitHandle[] ,每个人等待一个Task ,当任务成功执行时(并按照你的预期得到结果),你发出相应的WaitHandle信号。

您可以使用ManualResetEvent ,这会使您的isFound属性变得多余,例如

 private static ManualResetEvent found = new ManualResetEvent(false); ... Task.Factory.StartNew(() => Find(paramA, paramB)); Task.Factory.StartNew(() => Find(paramC, paramD)); Task.Factory.StartNew(() => Find(paramE, paramF)); Task.Factory.StartNew(() => Find(paramG, paramH)); var result = found.WaitOne(TimeSpan.FromSeconds(10)); // wait with timeout of 10 secs // do something with result ... private static bool Find(int[,] m1, int[,] m2) { if (found.WaitOne(0)) // check whether MSE has already been set return false; // Do work... found.Set(); }