如果我理解你的问题,你的重点是启动所有任务,让它们都并行运行,但确保返回值的处理顺序与启动任务的顺序相同。
查看规范,C# 8.0 Asynchronous Streams 任务排队等待并行执行但顺序返回可以查看像这样。
/// Demonstrates Parallel Execution - Sequential Results with test tasks
async Task RunAsyncStreams()
{
await foreach (var n in RunAndPreserveOrderAsync(GenerateTasks(6)))
{
Console.WriteLine($"#{n} is returned");
}
}
/// Returns an enumerator that will produce a number of test tasks running
/// for a random time.
IEnumerable<Task<int>> GenerateTasks(int count)
{
return Enumerable.Range(1, count).Select(async n =>
{
await Task.Delay(new Random().Next(100, 1000));
Console.WriteLine($"#{n} is complete");
return n;
});
}
/// Launches all tasks in order of enumeration, then waits for the results
/// in the same order: Parallel Execution - Sequential Results.
async IAsyncEnumerable<T> RunAndPreserveOrderAsync<T>(IEnumerable<Task<T>> tasks)
{
var queue = new Queue<Task<T>>(tasks);
while (queue.Count > 0) yield return await queue.Dequeue();
}
可能的输出:
#5 is complete
#1 is complete
#1 is returned
#3 is complete
#6 is complete
#2 is complete
#2 is returned
#3 is returned
#4 is complete
#4 is returned
#5 is returned
#6 is returned
实际上,这种模式似乎没有任何新的语言级别支持,而且由于异步流处理IAsyncEnumerable<T>,这意味着基础Task 在这里不起作用,并且所有工作人员async 方法都应该具有相同的Task<T> 返回类型,这在一定程度上限制了基于异步流的设计。
因此并根据您的情况(您希望能够取消长时间运行的任务吗?是否需要按任务处理异常?是否应该限制并发任务的数量?)这可能是有道理的查看@TheGeneral 的建议。
更新:
请注意,RunAndPreserveOrderAsync<T> 不一定必须使用 Queue 的任务 - 选择这只是为了更好地显示编码意图。
var queue = new Queue<Task<T>>(tasks);
while (queue.Count > 0) yield return await queue.Dequeue();
将枚举数转换为List 会产生相同的结果; RunAndPreserveOrderAsync<T> 的正文可以在这里替换为一行
foreach(var task in tasks.ToList()) yield return await task;
在此实现中,重要的是首先生成和启动所有任务,这与Queue 初始化或tasks 可枚举到List 的转换一起完成。但是,可能很难拒绝像这样简化上面的foreach 行
foreach(var task in tasks) yield return await task;
这将导致任务按顺序执行而不是并行运行。