Parallel.ForEach用法

时间:2017-04-28 14:39:50

标签: c# asp.net asynchronous

我有以下内容:

[HttpPost]
public async Task<IEnumerable<PlotAutocompleteModel>> Get()
{
     IEnumerable<PlotDomain> plots = await plotService.RetrieveAllPlots();
     var concurrent = ConcurrentQueue<PlotAutoCompleteModel>();
     Parallel.ForEach(plots, (plot) =>
     {
          concurrent.Enqueue(new PlotAutocompleteModel(plot);
     });

     return concurrent;
}

使用此功能,大约需要两秒钟。与:return plots.Select(plot => new PlotsAutocompleteModel(plot)).ToList();相比,大约需要四秒半。

但我总是被告知,对于域模型到视图模型的简单转换,Parallel.ForEach并不理想,主要是因为它应该用于更复杂的代码。我的用法显然不适用。

澄清:您将使用更多资源,例如您有一个位图,数量很大,您必须光栅化并从中创建新图像。

这是此代码的正确选项吗?由于我正在迭代然后转换的原始记录数量,我清楚地看到了性能提升。是否有更好的方法存在?

更新

public class ProductAutocompleteModel
{
     private readonly PlotDomain plot;
     public ProductAutocompleteModel(PlotDomain plot)
     {
          this.plot = plot;
     }

     public string ProductName => plot.Project.Name;
     // Another fourteen exist.
}

1 个答案:

答案 0 :(得分:3)

  

使用此功能,大约需要两秒钟。相比......大约四秒半。

     

但我总是被告知,对于将域模型简单转换为视图模型,Parallel.ForEach并不理想,主要是因为它应该用于更复杂的代码。

是的,嗯......没有办法 - 绝对没办法 - “将域模型简单转换为视图模型”应该需要四秒半。那里有一些严重的错误。它应该花费大约半毫秒左右。因此,您的PlotAutocompleteModel构造函数正在执行类似10,000 的工作量。

  

这是此代码的正确选项吗?我清楚地看到由于我正在迭代然后转换的原始记录数量而导致的性能提升。

可能不是,因为你在ASP.NET上托管。如果在ASP.NET上使用并行性,您将看到单个请求完成得更快,但它会对Web服务器的可伸缩性产生负面影响。出于这个原因,我从不推荐ASP.NET处理程序中的并行性。 (在某些情况下可以接受 - 例如非公共服务器,您知道您对同时用户的数量有一个硬性上限 - 但作为一般规则,它不是好主意)。

由于您的PlotAutocompleteModel构造函数的几个数量级的时间超过了预期,我怀疑它正在阻止I / O作为其工作的一部分。这里最好的解决方案是将阻塞I / O更改为异步I / O,然后使用并发异步,如下所示:

class PlotAutocompleteModel
{
  public static async Task<PlotAutocompleteModel> CreateAsync(PlotDomain plot)
  {
    ... // do asynchronous I/O to create a PlotAutocompleteModel.
  }
}

[HttpPost]
public async Task<IEnumerable<PlotAutocompleteModel>> Get()
{
  IEnumerable<PlotDomain> plots = await plotService.RetrieveAllPlots();
  var tasks = plots.Select(plot => PlotAutocompleteModel.CreateAsync(plot));
  return await Task.WhenAll(tasks);
}