【问题标题】:Why does async slow down independent, subsequent code?为什么异步会减慢独立的后续代码?
【发布时间】:2016-08-16 20:55:22
【问题描述】:

我有一个方法,由我的 UI 线程触发,可以加载一些数据。 UI 线程不会立即需要数据。大部分数据来自数据库,部分数据是从文件中读取的。使数据库异步读取后,文件读取变慢了。

这就是我原来的加载方法的样子:

public void load() {
  var sw = new Stopwatch();

  sw.Start();
  db1.loadDB();
  sw.Stop();
  Console.WriteLine("DB1: " + sw.Elapse.Milliseconds);

  sw.Restart();
  db2.loadDB();
  sw.Stop();
  Console.WriteLine("DB2: " + sw.Elapse.Milliseconds);

  sw.Restart();
  db3.loadDB();
  sw.Stop();
  Console.WriteLine("DB3: " + sw.Elapse.Milliseconds);

  sw.Restart();
  file.loadFile();
  sw.Stop();
  Console.WriteLine("File: " + sw.Elapse.Milliseconds);
}

因为每个数据库 loadDB 方法都执行多个数据库查询,所以我认为异步实现它们可能是个好主意。

public void load() {
  var sw = new Stopwatch();

  sw.Start();
  taskDB1 = db1.loadDBAsync();
  sw.Stop();
  Console.WriteLine("DB1: " + sw.Elapse.Milliseconds);

  sw.Restart();
  taskDB2 = db2.loadDBAsync();
  sw.Stop();
  Console.WriteLine("DB2: " + sw.Elapse.Milliseconds);

  sw.Restart();
  taskDB3 = db3.loadDBAsync();
  sw.Stop();
  Console.WriteLine("DB3: " + sw.Elapse.Milliseconds);

  sw.Restart();
  file.loadFile();
  sw.Stop();
  Console.WriteLine("File: " + sw.Elapse.Milliseconds);
}

任务在另一个方法中等待,当 UI 线程实际需要数据时调用。

困扰我的是 loadFile 方法执行所花费的时间。在 loadDB 的异步实现之前,它们每个都需要大约 100 毫秒才能完成,而 loadFile 方法大约需要 20 毫秒才能完成。 loadDBAsync 的异步实现大约需要 5ms 才能完成。但在第二个示例中,loadFile 方法需要 50 毫秒才能完成。我根本没有更改 loadFile 方法。

怎么可能,异步数据库访问会减慢读取文件的速度?

编辑: 添加了我用来测量时间的代码。 数据库在同一个磁盘上,至少现在是这样。 5ms是提交任务。

【问题讨论】:

  • 如果你的数据库和文件在同一个磁盘上。你可能会遇到 IO 瓶颈。你确定你的 loadDBAsync 只用了 5 毫秒,还是提交任务用了 5 毫秒。跨度>
  • 首先:您是如何衡量这些时间的?这已经是分析的重要部分。第二:请注意,您的 CPU 仍然必须完成所有相同的工作,并且 IO 工作也仍然相同。使数据库调用异步时的不同之处只是发生这种情况的时刻。因此,您需要考虑什么时候才能获得最佳性能。
  • 请向我们展示您用于执行测量计算的代码。

标签: c# asynchronous


【解决方案1】:

为什么异步会减慢独立的后续代码?

代码不是独立的。

它与并发执行的异步代码共享一个缓慢的资源:物理磁盘。

您的文件读取速度较慢,因为磁盘正在同时读取数据。

请注意,如果您有一个不可扩展的数据库,那么让您的数据库操作并发通常是没有意义的。您要做的就是导致磁盘抖动,并且很可能最终花费更长。使用好的 SSD,您可能会收支平衡,但并发数据库访问几乎不可能更快

【讨论】:

    【解决方案2】:

    正如BugFinder 在他的评论中提到的,您可能遇到了 IO 瓶颈。

    在第一种情况下,您要等到所有数据库操作完成后才能访问文件。

    在第二种情况下,您可能有多达四个线程同时访问磁盘,很可能是不同的扇区。因此,每次有线程上下文切换时,您都必须旋转磁盘,而且可能有很多次。

    尝试:

    public async Task load()
    {
        //all your DB access logic as previously
    
        await Task.WhenAll(new [] { taskDB1, taskDB2, task DB3 });
    
        file.loadFile();
    }
    

    我猜,loadFile 的性能与第一个版本相当。

    【讨论】:

    • 我同意这很可能是问题所在,但我不想在这种方法中等待数据库操作完成。在数据库操作之前执行文件读取似乎可以解决问题。
    • @Cedric 好的,请在您的问题中明确说明您正在寻找解决方案,因为您问的是“为什么它慢”,而不是“如何让它更快”,以及 BugFinder 的评论 +我的回答几乎涵盖了这个主题。
    猜你喜欢
    • 2017-01-16
    • 2018-11-30
    • 2018-03-11
    • 2012-01-02
    • 1970-01-01
    • 2013-06-28
    • 2019-01-03
    • 2012-12-27
    • 2018-01-02
    相关资源
    最近更新 更多