如何正确使用 Task.WhenAll

4

以下是我想使用TaskCompletionSource和Task.WhenAll等待任何任务首次返回True的代码(参考此问题及其答案):

TaskCompletionSource<bool> tcs = new TaskCompletionSource<bool>();
Task<bool> t0 = Task.Factory.StartNew<bool>(() => Find(paramA, paramB);
Task<bool> t1 = Task.Factory.StartNew<bool>(() => Find(paramC, paramD);
Task<bool> t2 = Task.Factory.StartNew<bool>(() => Find(paramE, paramF);
Task<bool> t3 = Task.Factory.StartNew<bool>(() => 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;

当任何任务返回true时,它可以很好地工作,但是如先前的答案所注意到的:

棘手的部分是注意到所有任务都返回false的情况...在.NET 4.5中,通过创建另一个任务(使用Task.WhenAll)会相对容易。

所以我尝试使用Task.WhenAll,但是我不想正确使用它...
我尝试了这样的代码:

tcs.Task.Wait(); // stays block here when all tasks return false
Task tr = Task.WhenAll(new Task[] { t0, t1, t2, t3, t4 });

if (tr.IsCompleted)
   return false;
else
return tcs.Task.Result;

感谢您的帮助。

如何使用 Task.WhenAll 来获取结果,即使所有任务都返回 false? - Florian
2个回答

5

你希望等待两个任务中的任意一个完成,这就是 Task.WaitAny() 的作用:

Task tr = Task.WhenAll(new Task[] { t0, t1, t2, t3, t4 });
Task.WaitAny(tcs.Task, tr);

if (tcs.Task.IsCompleted)
    return tcs.Task.Result;

return false;

这也修复了你的代码中的竞态条件:tr.IsCompleted 可能为 true,即使一些任务返回了 true,因为所有任务都可能同时完成。
作为替代方案,如果您不想阻塞,可以使用 Task.WhenAny()
但由于您正在使用 .Net 4.5,您可以使用awaitInterleaved() 方法一起使用,该方法会按完成顺序排序任务
async Task<bool> AnyTrue(IEnumerable<Task<bool>> tasks)
{
    foreach(var bucket in Interleaved(tasks))
    {
        if (await await bucket)
            return true;
    }

    return false;
}

2

当结果为false时,您可以使用以下方法进行检查:

if (!task.Result)
{ 
    // Do stuff...
}

这将检查布尔结果是否为false。要更完整,您可以执行以下操作:

asyncTask.ContinueWith(task =>
{
    // Check task status.
    switch (task.Status)
    {
        case TaskStatus.RanToCompletion:
            if (asyncTask.Result)
            {
                // returned true.
            }
            else
            {
                // returned false
            }
            break;
        default:
            break;
    }
}

我希望这能有所帮助。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接