【问题标题】:Need help refactoring this basic async-await loop需要帮助重构这个基本的异步等待循环
【发布时间】:2012-07-10 01:00:37
【问题描述】:

所以我有一个支持“保存”方法的业务对象,该方法对某些设备执行一些 IO。然后我有一个我想批量异步保存的对象的列表。我的代码现在看起来像这样:

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

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

【问题讨论】:

  • 如果 Save() 进行 I/O,CPU 开销真的很重要吗?
  • 你能把 foreach 放在不同的方法中,然后在 await 之后调用吗?
  • 只有前一个元素已经保存后才能保存下一个元素吗?
  • @millimoose 就我而言,这可能是因为我在 Save-method 中进行了脏检查。如果对象不脏,则不会执行任何 I/O - 因此这些调用的主要 CPU 时间将是 async/await-padding。如果我有一个包含 10000 个对象的列表,并且只有 1 个是脏的,我最终会得到 9999 个不必要的异步等待,我怀疑这会很重要。
  • @marvc1 好问题。这两种情况都可能相关,但主要是我想按顺序进行(“是”)。

标签: c# async-await


【解决方案1】:

好吧,你可以在所有事情上调用Save(),然后等待他们使用Task.WhenAll完成:

public async Task Save()
{
    await Task.WhenAll(Elements.Select(x => x.Save());
} 

或者如果你真的什么都不做,那就:

public Task Save()
{
    return Task.WhenAll(Elements.Select(x => x.Save());
} 

编辑:如果您想连续执行它们,请使用您已经获得的代码。值得注意的是,async/await 的设计方式是等待 实际上 同步完成的调用(例如缓存命中,或者在您的情况下进行脏检查)非常便宜。它不需要做任何任务调度、创建延续或类似的事情。你说:

如果我有一个包含 10000 个对象的列表,并且只有 1 个是脏的,我最终会得到 9999 个不必要的异步等待,我怀疑这会很重要。

与以往一样,关于性能瓶颈的怀疑几乎没有意义 - 重要的是关于性能瓶颈的证据。您是否尝试过现有代码并测量成本?如果没有,我强烈建议您在更改任何内容之前这样做。

【讨论】:

  • 从技术上讲,这回答了我的问题,因为我最初并没有指定我想连续进行。如果保存调用的顺序很重要,您是否有解决相同问题的方法?
  • @Nilzor:如果您想连续执行它们,那么使用给定的 API 对我来说这听起来是不可能的。毕竟,不等待第一个结果,你怎么知道什么时候开始下一个电话呢?将根据您的问题 cmets 更新评论。
  • 只是好奇,在切换到使用 Task.WhenAll 之后,为什么将 Save() 保持为异步并在 WhenAll 上使用 await 而不是只返回 WhenAll 任务?一个对另一个有好处吗?
  • @JamesManning:不是特别 - 这是我总是忘记的一个选项。将进行编辑以使其更清晰。
猜你喜欢
  • 1970-01-01
  • 2019-10-28
  • 1970-01-01
  • 2019-06-07
  • 2019-08-23
  • 2021-01-26
  • 1970-01-01
  • 2019-12-19
  • 2013-05-24
相关资源
最近更新 更多