如何从另一个线程同步UI和访问对象?

时间:2011-11-17 14:46:03

标签: c# wpf thread-safety

我对并行编程和线程都很陌生。我想计算并添加系列到图表,遗憾的是这是一项非常耗时的任务。所以我想在此期间显示一个加载屏幕:

(.NET 4.0, WPF for UI)

ShowLoadingScreen(true);
CalculateAndUpdateChart(chartControl, settings);
ShowLoadingScreen(false);
...
private void ShowLoadingScreen(bool show) { loadingScreen.IsBusy = show; }
private void CalculateAndUpdateChart(ChartControl chart, ProductSettings settings)
{
    chart.SomeSettings = ...
    foreach(var item in settings.Items)
    {
        chart.Series.Points.Add(CalculateItem(item));
        ...
    }
}

但当然这不起作用。所以我想我需要在另一个线程中更新Chart控件。

ShowLoadingScreen(true);
Tash.Factory.StartNew(()=>{CalculateAndUpdateChart(chartControl, settings)});
ShowLoadingScreen(false);

然而,现在我得到了不同的错误,大多数我无法从另一个线程访问chartControl和设置。

如何从另一个线程访问和更改UI,以及如何将在一个线程中创建的对象传递给另一个线程?你能给我一个类似的例子来说明我想做什么吗?

2 个答案:

答案 0 :(得分:2)

从非UI线程更新UI线程上的控件,您必须执行以下操作:

Dispatcher.Invoke(...);  OR
Dispatcher.BeginInvoke(...);

从这里开始: Build More Responsive Apps With The Dispatcher
还有一点:Beginners Guide to Threading in .NET: Part 5 of n
还有一个小例子:Task Parallel Library: 1 of n

答案 1 :(得分:0)

你必须做这个

修改/更新

这对我来说很好,但是de gui线程在计算时仍然被阻止

using System.Threading;
using System.Threading.Tasks;
using System.Windows;

namespace TPLSpielwiese
{
  /// <summary>
  /// Interaction logic for MainWindow.xaml
  /// </summary>
  public partial class MainWindow : Window
  {
    public MainWindow() {
      this.InitializeComponent();
    }

    private void Button_Click(object sender, RoutedEventArgs e) {
      TaskScheduler taskUI = TaskScheduler.FromCurrentSynchronizationContext();
      Task.Factory
        .StartNew(() =>
                    {
                      this.ShowLoadingScreen(true);
                    }, CancellationToken.None, TaskCreationOptions.None, taskUI)
        .ContinueWith((t) =>
                        {
                          //CalculateAndUpdateChart(chartControl, settings);
                          Thread.Sleep(1000);
                        }, CancellationToken.None, TaskContinuationOptions.None, taskUI)
        .ContinueWith((t) =>
                        {
                          this.ShowLoadingScreen(false);
                        }, CancellationToken.None, TaskContinuationOptions.None, taskUI);
    }

    private Window loadScreen;

    private void ShowLoadingScreen(bool showLoadingScreen) {
      if (showLoadingScreen) {
        this.loadScreen = new Window() {Owner = this, WindowStartupLocation = WindowStartupLocation.CenterOwner, Width = 100, Height = 100};
        this.loadScreen.Show();
      } else {
        this.loadScreen.Close();
      }
    }
  }
}