C# 正在等待某些任务完成(Task.WhenSome)

C# 正在等待某些任务完成(Task.WhenSome),c#,task,task-parallel-library,C#,Task,Task Parallel Library,我正在编写一个服务,它将来自各种互联网来源的数据组合在一起,并动态生成响应。速度比完整性更重要,因此,我希望在一些(并非所有)互联网来源作出回应后尽快做出回应。通常,我的服务会创建10个并发web请求,并在其中5个请求完成后停止等待并开始处理。无论是.NET Framework还是我所知道的任何第三方库都没有提供此功能,因此我可能不得不自己编写。我尝试实现的方法具有以下签名: public static Task<TResult[]> WhenSome<TResult>(

我正在编写一个服务,它将来自各种互联网来源的数据组合在一起,并动态生成响应。速度比完整性更重要,因此,我希望在一些(并非所有)互联网来源作出回应后尽快做出回应。通常,我的服务会创建10个并发web请求,并在其中5个请求完成后停止等待并开始处理。无论是.NET Framework还是我所知道的任何第三方库都没有提供此功能,因此我可能不得不自己编写。我尝试实现的方法具有以下签名:

public static Task<TResult[]> WhenSome<TResult>(int atLeast, params Task<TResult>[] tasks)
{
    // TODO
}
预期产出:

结果:10,20


在本例中,应忽略返回值
30
的最后一个任务(甚至不等待),因为我们已经获得了想要的结果数(2个结果)。出于同样的原因,出现故障和被取消的任务也应该被忽略。

这是一些笨重的代码,我认为可以满足您的要求。这可能是一个起点

它也可能是处理任务的一种糟糕方式和/或不是线程安全的,和/或只是一个糟糕的想法。但我希望如果是这样,有人会指出这一点

async Task WhenSome(至少int,列出任务)
{
List completedTasks=新列表();
int completed=0;
列表异常=新列表();
while(已完成<至少&tasks.Any()){
var completedTask=wait Task.wheny(任务);
任务。删除(已完成的任务);
if(completedTask.IsCanceled)
{
持续
}
if(completedTask.IsFaulted)
{
添加(completedTask.Exception);
持续
}
已完成++;
completedTasks.Add(completedTask);
}
如果(已完成>=至少)
{
返回completedTasks.Select(t=>t.Result).ToArray();
}
抛出新的AggregateException(异常).flatte();
}

所以。。。“你在解决这个问题上做了什么样的尝试?”伊恩·坎普我看了一下,希望能得到一些想法。在内部,它调用一个方法
internalwheall
,该方法返回类的实例,调用类
Task
的内部方法
AddCompletionAction
,依此类推。所有这些都是复杂的东西,对我想要实现的目标并没有真正的帮助。在最低级别,可以使用a来等待,直到记录了一定数量的偶数。以类似的方式工作,但可以等待。您描述的内容可以看作是处理事件。这意味着您可以使用反应式扩展“
Take(5)
异步等待任务序列中的5项完成。使用
ToObservable()
@Panagiotis Kanavos AFAIK信号量可以将任务转换为可观察的对象来限制并发性,在我的情况下,我当然不想限制并发性!尽管我对RX的体验几乎不存在,但我还是会尝试你的建议,看看它是否适用于我。谢谢@stuartd,这太棒了!调用
Task.WhenAny
在循环中是有创意的,可能足以满足我的需要。我看不到任何线程安全问题,因为逻辑是线性的。小改进:我在抛出
聚合异常的行中添加了
.flant()
,以避免处理层次结构。我要等几个小时才能把你的答案记为已被接受。
var tasks = new Task<int>[]
{
    Task.Delay(100).ContinueWith<int>(_ => throw new ApplicationException("Oops!")),
    Task.Delay(200).ContinueWith(_ => 10),
    Task.Delay(Timeout.Infinite).ContinueWith(_ => 0, new CancellationTokenSource(300).Token),
    Task.Delay(400).ContinueWith(_ => 20),
    Task.Delay(500).ContinueWith(_ => 30),
};
var results = await WhenSome(2, tasks);
ConsolePrint($"Results: {String.Join(", ", results)}");