【问题标题】:Run compute heavy task in parallel without waiting for result in C#并行运行计算繁重任务而不等待 C# 中的结果
【发布时间】:2019-11-23 20:04:22
【问题描述】:

我正在为应该并行执行耗时(每个请求 50 毫秒)代码的 HTTP 请求创建自定义实现处理程序。我不需要向用户返回任何东西,所以我唯一关心的是在单独的 CPU 内核上快速执行。这是配置实现:

public void Configure(IApplicationBuilder app){
  app.Run(context => {
    return Task.Run(async () => {
      await executeHandle(context);
    });
  });
}

在每次请求时,都会调用 executeHandle。

private static readonly Object obj = new Object();
public HashSet<string> arrayOfStrings = new HashSet();

public async Task executeHandle(HttpContext context){
  if (context.Request.Body != null){
    using (var ms = new MemoryStream()){
      await Microsoft.AspNetCore.Http.Extensions.StreamCopyOperation.CopyToAsync(
          context.Request.Body, ms, s_maxInMemoryData, context.RequestAborted);
      var requestBody = ms.ToArray();
      string html = Encoding.UTF8.GetString(responseBody);
      // inspect arrayOfStrings HashSet on each request and execude logic based on
      // items found
      // build DOM tree using HTMLAgilePack or some other library, takes 50ms
      // should execute in parallel, not just on a separate thread
      // call BuildDomTree();
    }
  }
}

public static void BuildDomTree(){
  // build DOM and update HashSet
  lock(obj){
   arrayOfStrings.add("somestring")
  }
}

由于性能在这里很重要,我想听听一些专家对如何调用 BuildDomTree 的意见。请注意,我不会向用户返回任何内容,但仍需要尽快获得结果。一种选择是使用

Task.Run(() => {BuildDomTree()});

这样做的问题是它只在不同的线程上执行,但不一定是并行的。另一种选择是使用Parallel.For 并将其包装在Task.Run 中以避免阻塞:

Task.Run(() => {Parallel.For(0, 1, BuildDomTree)});

我是否考虑过优化?有没有更好的方法来并行执行单个函数?

【问题讨论】:

  • ASP.NET 管道已经是异步的,所以只需 app.Run(context =&gt; executeHandle(context));。使用await Task.Run(() =&gt; BuildDomTree()); 以便线程池线程执行它而不是ASP 线程。然后,即使您的服务器可以处理 10 个并发活动请求,您仍然可以有 100 或 1000 个这些请求“运行”,并且线程池的大小将决定有多少 BuildDomTrees 实际并行执行。 arrayOfStrings 看起来需要是静态的。
  • 有点相关,考虑使用并发集合来容纳您的arrayOfStrings。如果您不特别需要HashSet&lt;string&gt; 的性能优势,则类似ConcurrentBag&lt;string&gt; 的东西会起作用。并发集合是线程安全的,这意味着您无需在添加或读取项目之前手动锁定列表。
  • 您确定BuildDomTree 是可并行化的吗?并非所有计算机问题都是embarrassingly parallel
  • 谢谢大家。想法是BuildDomTree 应该在不同的 CPU 上并行运行。它没有什么可并行化的,只是需要不减慢与 executeHandle 正在运行的同一 CPU 上的其他线程。
  • 那么您对Task.Run(() =&gt; {BuildDomTree()}); 的担心是它可能会在不同的线程中调用BuildDomTree 方法,而不是在不同的CPU 中?

标签: c# parallel-processing async-await task-parallel-library


【解决方案1】:

// 应该并行执行,而不仅仅是在单独的线程上

您几乎从不希望并行代码在您的 ASP.NET 服务器上运行。您可以很快使您的线程饿死,从而破坏您的服务器响应其他请求的能力。

但是,如果您绝对确定这是您想要做的,那么您需要使您的 BuildDomTree 自身并行,最好使用 Parallel 或并行 LINQ。执行Parallel.For(0, 1, ...) 毫无意义——它不会增加任何并行性,因为Parallel.For 中只有一个索引值。

然后是 ASP.NET 上的“一劳永逸”的问题,这很危险。真正的“一劳永逸”意味着“我不在乎什么时候结束,如果结束,或者是否失败”,这极为罕见。除非您采取措施通知 ASP.NET 您的后台工作,否则它可以在没有警告、没有异常和没有日志的情况下终止。

【讨论】:

    猜你喜欢
    • 2019-02-28
    • 2020-12-07
    • 1970-01-01
    • 2015-10-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多