【问题标题】:Can this code be improved with the use of multithreading?可以使用多线程改进此代码吗?
【发布时间】:2011-10-22 11:21:25
【问题描述】:

我有一个简单的 Windows 服务,它每天只运行一次。它在数据库中执行一些查询,生成适当的 html 内容(表格、div、...)并将其以电子邮件正文的形式发送给多个收件人。

电子邮件的正文是这样创建的:

private static string GenerateBody()
{
    using (var stringWriter = new StringWriter())
    using (var htmlWriter = new HtmlTextWriter(stringWriter))
    {
        htmlWriter.RenderBeginTag("html");
        htmlWriter.RenderBeginTag(HtmlTextWriterTag.Head);
        htmlWriter.WriteLine("<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />");
        htmlWriter.RenderEndTag();
        htmlWriter.RenderBeginTag("body");

        htmlWriter.Write(
            new StringBuilder()
                .Append(OverviewParagraph.GenerateHTMLContent())
                .Append(PackageWeightParagraph.GenerateHTMLContent())
                .Append(BoxWeightParagraph.GenerateHTMLContent())
                .Append(CodeQualityParagraph.GenerateHTMLContent())
                .Append(ChecksParagraph.GenerateHTMLContent())
                .ToString()
        );

        htmlWriter.RenderEndTag();
        htmlWriter.RenderEndTag();

        return stringWriter.ToString();
    }
}

所有GenerateHTMLContent 方法几乎相同——它们在我的数据库中执行查询,在 HTMLTextWriter 的帮助下构建 HTML 表,然后将表作为字符串返回。

能否通过使用多线程或异步等待模式来改进此代码?有问题的代码是我将行附加到 StringBuilder 对象的位置。

编辑:我问这个问题是因为我以前从未使用过多线程,只是想知道它是否可能。此外,程序现在运行速度也够快了。

【问题讨论】:

  • 是循环调用这个方法的代码吗?即我猜你正在循环一个收件人列表并调用上面的代码来生成电子邮件,这是正确的吗?
  • 重构的第一步可能是使用一个 Using() 而不是 2。另一个问题是,您是否知道或认为那些方法 GenerateHTMLContent 是耗时的操作?首先,您是否进行了一些基准测试以了解它们是否真的是最棒的?
  • 不太可能。线程有助于避免冻结用户界面(异步/等待)或在多核 CPU 上为您购买更多 CPU 周期。一天一次的程序不太可能有用户界面。而且这段代码需要更多的 dbase 服务器和网络带宽,而不是 cpu 周期。
  • @KevinHolditch - 不,这只运行一次。此方法生成 MailMessage 的正文。所有收件人都会收到相同的邮件。并使用 MailMessage.To.Add("...") 方法将收件人添加到 MailMessage 对象中。
  • @zenwalker - 请看我的编辑。

标签: c# multithreading async-await


【解决方案1】:

如果它只生成一件事,则并行化很复杂,因为您需要考虑同步化。当您可以执行 task 并行化(并行完成的单独和隔离操作)时,并行化是更明显的候选者。您也没有提供足够的信息来表明是否需要进行复杂的工作:

  • 现在需要多长时间?
  • 花费这么多时间是个问题吗?

如果有显着的好处(证明付出巨大努力的理由),那么当然可以!然而,我强烈怀疑答案是否定的,在这种情况下,别管它。在单个操作中处理多个线程很复杂。

您也许可以将单独的文档部分视为并行任务,但 HTML 生成通常很快 - 所以除非您对此进行了分析并且知道它们需要时间,否则不要打扰。更有可能:您的数据查询是障碍。在这种情况下,请花一些时间改进它,而不必担心并行化。

【讨论】:

    【解决方案2】:

    如果GenerateHTMLContent 方法是隔离的(即如果同时运行它们不会相互干扰),您可以将它们全部启动并在它们可用时收集结果:

    // start tasks
    Task<string> overviewParagraph =
        Task.Factory.StartNew( () => OverviewParagraph.GenerateHTMLContent() );
    
    Task<string> packageWeightParagraph =
        Task.Factory.StartNew( () => PackageWeightParagraph.GenerateHTMLContent() );
    
    ....
    
    // collect results
    string overviewParagraphHtml = overviewParagraph.Result;
    string packageWeightParagraphHtml = packageWeightParagraph.Result;
    ...
    

    【讨论】:

      【解决方案3】:
      StringBuilder sb = new StringBuilder();
      Parallel.Invoke(
          () => { var s = OverviewParagraph.GenerateHTMLContent(); lock (sb) sb.Append(s); },
          () => { var s = PackageWeightParagraph.GenerateHTMLContent(); lock (sb) sb.Append(s); },
          () => { var s = BoxWeightParagraph.GenerateHTMLContent(); lock (sb) sb.Append(s); },
          () => { var s = CodeQualityParagraph.GenerateHTMLContent(); lock (sb) sb.Append(s); },
          () => { var s = CodeQualityParagraph.GenerateHTMLContent(); lock (sb) sb.Append(s); }
      );
      

      【讨论】:

      • 现在假设我们希望文档部分以可预测的顺序出现:)
      • 仅当 slobodan 不关心这些部分以什么顺序添加到输出时。
      • 然后将结果分配给不同的变量并在Parallel.Invoke之后以您想要的任何顺序附加到sb
      猜你喜欢
      • 1970-01-01
      • 2011-05-27
      • 2013-11-05
      • 1970-01-01
      • 1970-01-01
      • 2013-10-26
      • 2018-02-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多