【发布时间】:2022-02-13 18:01:02
【问题描述】:
我正在编写一个服务,它结合了来自各种互联网来源的数据,并即时生成响应。速度比完整性更重要,所以我想在一些(不是所有)互联网资源响应后立即生成我的响应。通常,我的服务会创建 10 个并发 Web 请求,并且应该在其中 5 个完成后停止等待并开始处理。 .NET Framework 和我所知道的任何第三方库都没有提供此功能,因此我可能不得不自己编写它。我尝试实现的方法具有以下签名:
public static Task<TResult[]> WhenSome<TResult>(int atLeast, params Task<TResult>[] tasks)
{
// TODO
}
与Task.WhenAny 的工作方式相反,如果已获得所需数量的结果,则应吞下异常。但是,如果在完成所有任务后,没有足够的收集结果,则应抛出 AggregateException 以传播所有异常。
使用示例:
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);
Console.WriteLine($"Results: {String.Join(", ", results)}");
预期输出:
结果:10、20
在这个例子中,最后一个返回值30 的任务应该被忽略(甚至不等待),因为我们已经获得了我们想要的结果数量(2 个结果)。出于同样的原因,故障和取消的任务也应该被忽略。
【问题讨论】:
-
其实有多种方式。在最低级别,semaphore 可用于等待记录一定数量的事件。 This Latch implementation 以类似的方式工作,但可以等待。
-
您所描述的可以看作是处理事件。这意味着您可以使用 Reactive Extensions 的
Take(5)异步等待,直到任务序列中的 5 个项目完成。可以使用ToObservable()将任务转换为 Observables -
@Panagiotis Kanavos AFAIK 信号量用于限制并发,就我而言,我当然不想限制并发!我会用 Reactive Extensions 尝试你的建议,看看它是否能帮到我,尽管我在 RX 方面的经验实际上是不存在的。
-
@TheodorZoulias 您不必对其阻止,您可以等待该信号量从另一个线程触发
-
有些相关:Asynchronous Task.WhenAll with timeout。该问题是关于设置等待任务的时间限制,并且可以接受任意数量的已完成任务(即使是零完成的任务)。如果某些已完成的任务没有成功完成,甚至是可以接受的。相反,这个问题是关于成功完成的任务,并且限制的是它们的数量,而不是它们的持续时间。
标签: c# asynchronous task task-parallel-library