1

我有一个单线程程序,它执行复杂、耗时的计算并报告这些计算的进度。

    private void BtnExecute_Click()
    {
        ComplexCalculation(Object1);
        ComplexCalculation(Object2);
        ComplexCalculation(Object3);
    }

ComplexCalculation 方法如下所示:

private void ComplexCalculation(Object MyObject)
{
   (...do some time consuming operation on MyObject...);
   WriteToTextboxGUI("50% Complete" + MyObject.Name);
   (...do another time consuming operation on MyObject...);
   WriteToTextboxGUI("100% Complete" + MyObject.Name);
}

在上面的代码中,该WriteToTextboxGUI(string)方法使用进度更新 GUI 上的文本框。

我希望采用多线程方法,以便 GUI 在执行复杂计算时保持响应。我已经阅读了很多关于BackgroundWorkerThreads一般的内容,然后当TaskTPL 与 .Net 4.0 一起出现时,这是如何简化/改进的,现在如何Async以及Await已经与 .Net 4.5 一起出现,并且想知道这些是否最近的技术可以让我重新编写我的应用程序(以相对简单的代码),以便它可以:

  • 同时使用多个线程来执行复杂的计算
  • 像以前一样向我的 GUI 报告进度
  • 始终保持响应式 GUI

谁能指出一个满足这三个标准的简单解决方案的方向?

PS 这个应用程序不会在服务器上运行,它将在桌面上的 WPF 中运行。

4

2 回答 2

7

我建议您IProgress<T>用于进度报告和Task.Run启动后台任务。我最近完成了一个博客系列,显示Task.Run优于BackgroundWorker.

在你的情况下:

private sealed class ComplexCalculationProgress
{
  public string Name { get; set; }
  public int PercentComplete { get; set; }
}

private void ComplexCalculation(Object MyObject, IProgress<ComplexCalculationProgress> progress)
{
  (...do some time consuming operation on MyObject...);
  if (progress != null)
    progress.Report(new ComplexCalculationProgress { Name = MyObject.Name, PercentComplete = 50 });
  (...do another time consuming operation on MyObject...);
  if (progress != null)
    progress.Report(new ComplexCalculationProgress { Name = MyObject.Name, PercentComplete = 100 });
}

请注意,由于您的后台操作不再调用WriteToTextboxGUI,因此它具有更好的关注点分离。您会发现 的设计IProgress<T>鼓励在您自己的代码中进行更好的设计。

您可以这样称呼它(使用简单的并行性):

private async void BtnExecute_Click()
{
  var progress = new Progress<ComplexCalculationProgress>(update =>
  {
    WriteToTextboxGUI(update.PercentComplete + "% Complete " + update.Name);
  });
  await Task.WhenAll(
    Task.Run(() => ComplexCalculation(Object1, progress)),
    Task.Run(() => ComplexCalculation(Object2, progress)),
    Task.Run(() => ComplexCalculation(Object3, progress))
  );
}

或者,您可以轻松使用“真正的”并行性:

private async void BtnExecute_Click()
{
  var progress = new Progress<ComplexCalculationProgress>(update =>
  {
    WriteToTextboxGUI(update.PercentComplete + "% Complete " + update.Name);
  });
  var objects = new[] { Object1, Object2, Object3 };
  await Task.Run(() =>
      Parallel.ForEach(objects, o => ComplexCalculation(o, progress))
  );
}
于 2013-11-15T00:19:21.063 回答
0

后台工作人员可以轻松完成所有这些工作。

同时使用多个线程来执行复杂的计算

为每个计算使用一个新的后台工作人员。

像以前一样向我的 GUI 报告进度

将 WorkerReportsProgress=True 与 ReportProgress() 方法和 ProgressChanged 事件一起使用。

始终保持响应式 GUI

是的。

于 2013-11-14T23:44:38.030 回答