具有复杂背景计算的响应式GUI的多线程C# - async / await,TPL或两者兼而有之?

时间:2013-11-14 23:37:20

标签: c# asynchronous parallel-processing task-parallel-library

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

    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的内容,然后在Task和TPL与.Net 4.0一起进入现场时如何简化/改进了这一点,现在又如何AsyncAwait已经与.Net 4.5一起到了,并且想知道这些更新的技术是否允许我重新编写我的应用程序(使用相对简单的代码),以便它可以:

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

任何人都可以指出一个满足这三个标准的简单解决方案吗?

P.S。此应用程序不会在服务器上运行,它将在桌面上的WPF中运行。

2 个答案:

答案 0 :(得分:7)

我建议您使用IProgress<T>作为进度报告,Task.Run启动后台任务。我最近完成了一个博客系列,展示了Task.Run is superior to 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))
  );
}

或者,您可以轻松使用&#34; real&#34;并行:

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))
  );
}

答案 1 :(得分:0)

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

  

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

为每次计算使用新的后台工作程序。

  

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

使用ReportReportsProgress = True,使用ReportProgress()方法和ProgressChanged事件。

  

在整个

中维护响应式GUI

是的。