c# - 有没有办法等待所有任务直到特定结果为真,然后取消其余的?

标签 c# .net .net-core task-parallel-library

在我的 C# 控制台应用程序中,我试图运行多个同时执行各种数据检查的任务。 如果其中一项任务返回真值,我应该停止其他任务,因为我得到了可操作的结果。也很可能没有函数返回 true

我有一起运行这些任务的代码(我想),我只是在到达终点线时遇到了问题:

Task task1 = Task.Run(() => Task1(stoppingToken));
Task task2 = Task.Run(() => Task2(stoppingToken));
Task task3 = Task.Run(() => Task3(stoppingToken));
Task task4 = Task.Run(() => Task4(stoppingToken));
Task task5 = Task.Run(() => Task5(stoppingToken));
Task task6 = Task.Run(() => Task6(stoppingToken));

Task.WaitAll(task1, task2, task3, task4, task5, task6);

这与已知所需结果(超时值)的链接问题中的答案略有不同。我正在等待这些任务中的任何一个可能返回 true,然后取消剩余的任务(如果它们仍在运行)

Task.WhenAny with cancellation of the non completed tasks and timeout

最佳答案

这是一个基于连续任务的解决方案。这个想法是将延续任务附加到每个原始(提供的)任务,并在那里检查结果。如果匹配,则完成源将设置一个结果(如果没有匹配,则根本不会设置结果)。

然后,代码将等待最先发生的事情:要么所有延续任务完成,要么设置任务完成结果。无论哪种方式,我们都准备好检查与任务完成源关联的任务的结果(这就是为什么我们等待后续任务完成,而不是原始任务),如果已设置,则它是几乎表明我们有一场比赛(最后的额外检查有点偏执,但我猜比抱歉更安全......:D)

public static async Task<bool> WhenAnyHasResult<T>(Predicate<T> isExpectedResult, params Task<T>[] tasks)
{
    const TaskContinuationOptions continuationTaskFlags = TaskContinuationOptions.ExecuteSynchronously | TaskContinuationOptions.OnlyOnRanToCompletion | TaskContinuationOptions.AttachedToParent;
         
    // Prepare TaskCompletionSource to be set only when one of the provided tasks
    // completes with expected result
    var tcs = new TaskCompletionSource<T>();

    // For every provided task, attach a continuation task that fires
    // once the original task was completed
    var taskContinuations = tasks.Select(task =>
    {
        return task.ContinueWith(x =>
        {
            var taskResult = x.Result;
            if (isExpectedResult(taskResult))
            {
                tcs.SetResult(taskResult);
            }
        },
        continuationTaskFlags);
    });

    // We either wait for all the continuation tasks to be completed 
    // (it's most likely an indication that none of the provided tasks completed with the expected result)
    // or for the TCS task to complete (which means a failure)
    await Task.WhenAny(Task.WhenAll(taskContinuations), tcs.Task);

    // If the task from TCS has run to completion, it means the result has been set from
    // the continuation task attached to one of the tasks provided in the arguments
    var completionTask = tcs.Task;
    if (completionTask.IsCompleted)
    {
        // We will check once more to make sure the result is set as expected 
        // and return this as our outcome
        var tcsResult = completionTask.Result;
        return isExpectedResult(tcsResult);
    }

    // TCS result was never set, which means we did not find a task matching the expected result.
    tcs.SetCanceled();
    return false;
}

现在,用法如下:

static async Task ExampleWithBooleans()
{
    Console.WriteLine("Example with booleans");

    var task1 = SampleTask(3000, true);
    var task2 = SampleTask(5000, false);

    var finalResult = await TaskUtils.WhenAnyHasResult(result => result == true, task1, task2);

    // go ahead and cancel your cancellation token here

    Console.WriteLine("Final result: " + finalResult);
    Debug.Assert(finalResult == true);
    Console.WriteLine();
}

将它放入泛型方法的好处在于,作为原始任务的结果,它适用于任何类型,而不仅仅是 bool 值。

关于c# - 有没有办法等待所有任务直到特定结果为真,然后取消其余的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68749441/

相关文章:

c# - Observable.FromAsync 内存泄漏 [墨尔本开发者/Devicenet]

c# - C# 中的非托管 DLL 无法正常工作

.net-core - .NET Core 库中的 EF Core

asp.net-core - 是否可以将 ffmpeg 命令的输出流式传输到具有 dot net core 的客户端?

c# - 在 C# 中编写静态和非静态方法时如何避免 'call is ambiguous...' 错误?

c# - 在 C# 中从 context.httprequest 读取原始数据

c# - INotifyPropertyChanged 用户是否使用反射从实际属性中获取值?

c# - Target Framework 从 4.0 更改为 3.5 破坏了 EF 模型。错误 111 : Referential constraint errors

c# - 获取邮箱不可用。太多无效收件人错误

.net-core - .NET Core 中的 Azure Web 作业