C# 需要帮助重构这个基本的异步等待循环吗

C# 需要帮助重构这个基本的异步等待循环吗,c#,async-await,C#,Async Await,因此,我有一个支持“Save”方法的业务对象,该方法对某些设备执行一些IO。然后,我有一个要在批处理中异步保存的对象列表。我的代码现在如下所示: public async Task Save() { foreach (var element in Elements) { await element.Save(); } } 现在这导致了n等待次数,我知道每次等待都会导致一些CPU开销。我想消除

因此,我有一个支持“Save”方法的业务对象,该方法对某些设备执行一些IO。然后,我有一个要在批处理中异步保存的对象列表。我的代码现在如下所示:

    public async Task Save()
    {
        foreach (var element in Elements)
        {
            await element.Save();
        }
    }    

现在这导致了
n
等待次数,我知道每次等待都会导致一些CPU开销。我想消除这一点,只有一个等待。如何进行重构以实现这一点?

好吧,您可以对所有内容调用
Save()
,然后使用以下命令等待它们全部完成:

或者,如果您真的不做任何其他事情,只需:

public Task Save()
{
    return Task.WhenAll(Elements.Select(x => x.Save());
} 
编辑:如果您想按顺序执行,请使用您已有的代码。值得注意的是,async/await的设计方式,即等待实际同步完成的调用(例如缓存命中,或者在您的情况下是脏检查)非常便宜。它不需要做任何任务调度、创建延续或类似的事情。你说:

如果我有一个10000个对象的列表,并且只有1个对象是脏的,那么我最终将得到9999个不必要的异步等待,我怀疑这将是非常重要的


和以往一样,对性能瓶颈的怀疑几乎毫无意义——重要的是性能瓶颈的证据。您是否尝试过现有代码并衡量了成本?如果没有,我强烈建议您在更改任何内容之前执行此操作。

如果
Save()
执行I/O操作,CPU开销真的很重要吗?你能把foreach放在一个不同的方法中,然后在等待之后调用它吗?下一个元素只能在前一个元素已经保存的情况下保存吗?@millimoose在我的例子中,这可能是因为我在Save方法中做了脏检查。如果对象不是脏的,则不进行I/O操作-因此这些调用的主要CPU时间将是异步/等待填充。如果我有一个10000个对象的列表,而只有1个对象是脏的,那么我将得到9999个不必要的异步等待,我怀疑这将是一个重要的问题。@marvc1问得好。这两种情况可能都是相关的,但主要是我希望按顺序执行(“是”)。从技术上来说,这回答了我的问题,因为我最初没有指定我希望按顺序执行。如果save调用的顺序很重要,你有没有解决同样问题的方法?@Nilzor:如果你想按顺序进行调用,我觉得用给定的API是不可能的。毕竟,没有等待第一个结果,你怎么知道什么时候开始下一个电话?将根据您的问题注释进行更新。只是好奇,在切换到使用Task.WhenAll后,为什么要将Save()保持为异步并在WhenAll上使用wait,而不只是返回WhenAll任务?“一个对另一个有好处吗?”詹姆斯曼宁:不特别——这是我经常忘记的选择。将进行编辑以使其更清晰。
public Task Save()
{
    return Task.WhenAll(Elements.Select(x => x.Save());
}