我可以设置一个异步操作在另一个异步操作完成后自动发生吗?

问题描述 投票:0回答:2

我有一个有点奇怪的案例。我有一个看起来像这样的列表:

var myStuff = [A:1, A:2, A:3, B:1, B:2, C:1, C:2, C:3, C:4];

对于所有信件,我想为除最后一个版本之外的每个版本启动任务。我想在以前的版本完成后运行最后一个版本。为了更好地解释:

  • 为 A:1 运行任务
  • 为 A:2 运行任务
  • 当这两个任务完成后,运行 A:3 的任务

对 B 和 C 重复此操作,最好同时运行 A:1、B:1、C:1、A:2、B:2 等。

我想我可以使用 Linq Groupby 将列表分成 3 组,按版本排序,然后启动除最后一个之外的所有任务。我不知道如何以一种既可以让最后一个版本(A:3、B:2、C:4)等待之前的任务,同时又确保 A:1、B:1、 C:1、A:2、B:2、C:2 等都同时运行。

这可能吗?

c# .net linq
2个回答
0
投票

作为第一种方法,解决方案可能如下所示:

(char, int)[] myStuff = [('A', 1), ('A', 2), ('A', 3), ('B', 1), ('B', 2), ('C', 1), ('C', 2), ('C', 3), ('C', 4)];

var groups = myStuff.GroupBy(x => x.Item1).Select(HandleGroup);
await Task.WhenAll(groups);

static async Task HandleGroup(IGrouping<char, (char, int)> gr)
{
    var ordered = gr.OrderBy(x => x.Item2).ToArray();
    var prior = ordered.Take(ordered.Length - 1).Select(HandleTuple);
    await Task.WhenAll(prior);
    await HandleTuple(ordered.Last());
    
    async Task HandleTuple((char, int) tuple)
    {
        await Task.Delay(tuple.Item2 * 1000);
        Console.WriteLine($"Item: {tuple} has been handled");
    }
}  

将您的项目更改为元组,因为代码不正确。
希望它能向您展示这个想法。


0
投票

我将首先讨论一些基础知识。通常,在使用

async
时,您希望
await
您的任务:

await SomeMethodAsync(myData);

这里的第一个问题不仅仅是一个项目,而是一个完整的数组。即使假设您知道自己有多少物品,您也不会想做这样的事情:

await SomeMethodAsync(myData[0]);
await SomeMethodAsync(myData[1]);
await SomeMethodAsync(myData[2]);

这实际上让你重新按顺序运行事情。记住

async
方法总是返回一个任务,你可以这样做:

await Task.WhenAll(myData.Select(async item => SomeMethodAsync(item));

但我们还没有完全做到这一点。您需要排除 IEnumerable 中的最后一项,以便在其他项完成之前它不会运行。值得庆幸的是,C# 有一些很好的 范围和索引支持 使这变得更容易;

await Task.WhenAll(myData[..^1].Select(async item => SomeMethodAsync(item));
await myData[^1];

现在已经非常接近了,但这只是数据中的一组。让我们再次扩展它以支持多个组。

不幸的是,我不知道

A:1
A:2
B:1
等项目的结构。对于这段代码,我假设它们是字符串。这给了我足够的时间,我也可以开始使用示例问题中的
myStuff
变量:

var groups = myStuff.GroupBy(s => s.Split(':')[0])
await Task.WhenAll(async g => 
    groups.Select(async d => {
            // need an array or list instead of IGrouping enumerable for index support
            var data = d.ToList(); 
            await Task.WhenAll(data[..^1].Select(asycn item => SomeMethodAsync(item));
            await data[^1];
        });
);

需要获取上述代码中每个组的列表并不是很好,但是高效地从一组未知大小中排除最后一项是很棘手的(

Last()
对于这种情况来说不太好)。您可以通过缓冲来做到这一点,但在这种情况下,该代码的维护和复杂性成本可能超过了胜利。

© www.soinside.com 2019 - 2024. All rights reserved.