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

Cau*_*tix 1 c# parallel-processing asynchronous task-parallel-library

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

    private void BtnExecute_Click()
    {
        ComplexCalculation(Object1);
        ComplexCalculation(Object2);
        ComplexCalculation(Object3);
    }
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)

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

我正在寻求采用多线程方法,以便在执行复杂计算时GUI保持响应.我已经阅读了相当多的内容BackgroundWorker,Threads一般情况下,以及当TaskTPL与.Net 4.0一起进入现场时如何简化/改进,现在如何AsyncAwait已经到达.Net 4.5,并想知道这些是否更新的技术可以让我重新编写我的应用程序(使用相对简单的代码),以便它可以:

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

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

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

Ste*_*ary 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 });
}
Run Code Online (Sandbox Code Playgroud)

请注意,由于您的后台操作不再调用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))
  );
}
Run Code Online (Sandbox Code Playgroud)

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

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))
  );
}
Run Code Online (Sandbox Code Playgroud)