【问题标题】:Best performance using async and await使用 async 和 await 的最佳性能
【发布时间】:2013-11-25 00:58:29
【问题描述】:

我有一台 1 核机器,我想使用 async 和 await 来提高我的代码性能。 代码有两个主要部分。第一个IO操作。 (从 azure 服务总线队列中读取多个 sessionId),第二个,处理数据 - CPU 很重。 我已经将 DequeueAsync 方法包装在一个返回任务的异步方法中:

private async Task<SomeReturnValue> AcceptFromQueueAsync(){
  try{ 
       SomeReturnValue result = await DequeueAsync.configureAwait(false);
       return SomeReturnValue;  
  }
  catch{
   //logging and stuff
  }

CPU 重的方法是同步:DoSyncWork() 由于第二部分 CPU 很重,我不想使用并行性(实际上不能......) CPU 部分只有在 IO 部分完成后才能启动。 鉴于以上是以下实现与 1 cpu 机器的方式吗?

private void AcceptAndProcessWrapper(){
    //Count is some cosnt defined outside the method
    _acceptTasks = new List<Task<SomeReturnValue>>();
     for (var i = 0; i < Count; i++)
     {
                    _acceptTasks.Add(AcceptFromQueueAsync());
      }
      //The use of list is for convince in processing  
      Task.WaitAll(_acceptTasks.ToArray());
      //The CPU part 
       Task.Run(DoSyncWork).Wait();
 }

我知道我不能将 Task.Run() 用于同步部分,但我想允许在多个内核上实现功能(通过使用 Task.Run() 启动多个任务并将它们保存在一个数组中) 请问上面的执行。 (多次调用返回任务的异步方法)提高性能?或者我应该为每个异步调用启动一个新任务,例如 Task.Run(AcceptFromQueueAsync)?

【问题讨论】:

  • 听起来你可能想研究 TPL 数据流。

标签: c# multithreading asynchronous async-await


【解决方案1】:

异步运行第一部分可能有意义,取决于它的名称中包含 Async,这表明它无论如何都不会阻塞,在这种情况下不需要,但绝对不需要异步运行 DoSyncWork,因为它只有 1进程,您正在等待它完成。

【讨论】:

    【解决方案2】:

    您拥有的代码确实会并行执行异步出队。它将一次启动所有异步任务,然后等待它们全部完成后再继续。

    Task.Run 添加额外的呼叫将无济于事。在另一个线程中启动异步任务只是增加了额外的开销而没有任何附加值。 Task.Run 应该用于在另一个线程中运行 CPU 绑定工作,而不是在另一个线程中进行异步工作。

    【讨论】:

      【解决方案3】:

      谈到 Task(s) 的性能(如果您将来将它与 DoSyncWork 一起使用),您还应该记住 Task.Run 默认使用 ThreadPool,但预计线程池中的线程会做一些快速而小型的工作。如果任务确实是一些“繁重”的任务,您应该考虑在新创建的线程中运行它们,这也可能是强制执行以下操作:

      new Task(() => { }, TaskCreationOptions.LongRunning);
      

      编辑:

      在下面的讨论之后,我想再澄清一下。

      目前,Task.Run(DoSyncWork).Wait(); 确实没有任何意义(更重要的是,在另一个线程上运行任务可能需要额外的时间(甚至来自 ThreadPool 的),但是如果您将来想使用多个内核,您显然应该将该任务移动到另一个线程来运行(如果它确实占用大量 CPU,则考虑将任务作为“LongRunning”运行)(如果您的应用当然需要它(不确定是什么类型)您拥有的应用程序))。在这种情况下,仍然没有必要立即致电.Wait()

      【讨论】:

      • 这项工作并没有长期运行。启动一个异步任务几乎不需要任何时间,即使它需要很长时间才能完成。不需要手动构建这样的任务,也不需要“长时间运行”。
      • 嗯,这么大评价的人居然说出这样的话,真是奇怪。 ThreadPool(除了它的优点)也有缺点,众所周知,如果 ThreadPool 用完线程并创建新线程,它甚至可能需要 500 毫秒(!),这与新线程创建时间无法比拟。请同时阅读以下文章:stackoverflow.com/questions/9200573/….
      • 所有这些都与这里的情况无关操作没有长时间运行。运行只需几微秒,因为它只是启动任务等待它完成。首先,将这项工作放入线程池线程是完全没有必要的,如果您选择这样做,无论出于何种原因,它肯定不会花费很长时间;它会非常快
      • 一切都与发展相关。如果你给人们一些建议,你应该考虑到他们将来会使用它,但不仅仅是在这个地方。我不明白为什么我不能向用户指出性能细微差别,他可以进一步使用。如果他对我所说的内容感兴趣,他总是可以用 cmets 澄清这一点。如果没有 - 这可能对其他人有帮助。我更愿意为人们提供钓鱼竿,而不是仅仅喂鱼。
      • @Servy 别傻了。这不是“随机无关信息”。用户正在寻找方法建议,这符合条件。按照您的标准,Stephen Cleary 的评论也应该被否决。
      【解决方案4】:

      我使用与此类似的模式来尽量保持 IO 和 CPU 的运行

      public async void DoWork()
      {
          Task cpuBoundTask = null;
          Task ioBoundTask = null;
      
          while(true)
          {
               ioBoundTask = DoIOBoundStuff();
               var ioResult = await ioBoundTask;
      
               if(cpuBoundTask != null)
               {
                   await cpuBoundTask;
               }
               cpuBoundTask = DoCpuBoundStuff(ioResult);
          }
       }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-06-26
        • 1970-01-01
        • 2018-05-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-04-10
        相关资源
        最近更新 更多