WPF UI更新线程问题

时间:2013-03-07 13:37:52

标签: c# wpf multithreading asynchronous

更新

我已经解决了这个问题。我发现我没有正确地同步对线和图表对象的访问。我没有在处理程序的持续时间内锁定它们,而是将它们锁定在它们被使用的那一刻。我认为动态数据显示库中的一些东西是使用延迟执行,并且在我没想到的时候尝试访问其中一个对象。

TL; DR:正确同步锁。


我正在使用WPF在GUI上工作,以在一些类库中可视化算法。我在GUI中使用的所有控件都是标准WPF或来自Microsoft Dynamic Data Display库。

我得到了非常常见的“调用线程无法访问此对象,因为另一个线程拥有它”错误。我已经做了相当多的搜索,但我自己找不到解决方案。

现在,如果线程不拥有它,我对如何在图表中添加线条感到有点困惑。我已尝试使用调度程序,因为本网站上有许多其他答案已经建议,但它没有解决这个问题。


摘要窗口旨在成为主窗口的子窗口,该窗口在模拟运行之前打开并实时显示数据。

模拟器发布了许多事件,这些事件都是异步处理的(内部,它使用BeginInvoke和EndInvoke)。这会导致这种情况出现问题吗?

摘要视图模型注册了许多可视化,然后创建自己的控件并将它们添加到摘要窗口提供的画布中。

代码的简要概述如下

public class MainWindowModel
{
    //...
    public async void RunSimulation()
    {
        WindowService.OpenWindowFor<SimulationSummaryViewModel>(SimSummaryVM);
        await Simulator.Run();
    }
}

public class SimulatorSummaryViewModel
{
    //...
    public SimulatorSummaryViewModel()
    {
        Visualisations.Add(new RealTimeChart());
    }
    //...
}

public class RealTimeChart : IVisualisation
{
    // ...
    private ChartPlotter _Chart; //From Dynamic Data Display library

    //Handles adding information to the current line 
    private void OnSimulatorStepHandler(Args a)
    { 
      /* The exception occurs in here, when adding a data point, 
         whilst another handler is adding a line to the chart.
         I have tried adding a lock to the chart and line objects, but it had no effect
      */ 
    }

    //Handles adding the current line to the chart
    private void OnSimulatorRepetitionComplete(Args a)
    {
        //lineToAdd is an EnumerableDataSource<DataPoint>.
        //DataPoint is a simple class with two primitive properties.
        _Chart.Dispatcher.Invoke(new Action(()=>
        {
            _Chart.AddLineGraph(lineToAdd);
        }));
    }
}

public class SummaryWindow : Window
{
    // ...
    public SummaryWindow()
    {
        // ...
        foreach(IVisualisation Vis in ViewModel.Visualisations)
        {
            Vis.Draw(this.VisCanvas);
        }
    }
}

2 个答案:

答案 0 :(得分:2)

我假设从另一个线程调用OnSimulatorRepetitionComplete,在该线程中调用图表。

在WPF中,所有GUI操作都需要在Dispatcher Thread中完成。 在方法中的内容周围添加此代码:

Dispatcher.BeginInvoke(new Action(() =>
{

}));

所以它变成了这个:

private void OnSimulatorStepHandler(Args a)
{ 
    Dispatcher.BeginInvoke(new Action(() =>
    {
        // Add your code here
    }));
}

答案 1 :(得分:0)

我已经解决了这个问题,所以我会在这里发布我的解决方案。


由于模拟器发布的异步处理事件和可视化中隐含的同步处理(发生了多个步骤,然后重复结束,然后重复),需要更好地同步对图表和行的访问。以下是我的(真实)原始代码。

我正在使用的图表控件推迟使用seriesToAdd集合(引用行数据),直到添加图表。由于行数据(和图表)在处理程序的生命周期内未被锁定,因此有可能在未锁定(由我)但在图表控件中使用时访问行数据。

我不会发布生成的代码,因为它只是移动锁来封装完整的方法。

[STAThread]
private void OnRepetitionComplete(Evolve.Core.Simulation.RepetitionResult Result)
{
    //Convert the data series to something usable for the chart
    EnumerableDataSource<DataPoint> seriesToAdd = new EnumerableDataSource<DataPoint>(_obCurrentRepetitionDataSeries);

    //Translate the data point properties in to X&Y coords on the graph
    seriesToAdd.SetXYMapping(DP => new System.Windows.Point(DP.Step, DP.Number));

    //Get the line colour and add the line to the chart
    System.Windows.Media.Color lineColor = GetLineColor(Result.ReasonForCompletion);

    lock (chartLockObject)
    {
        lock (lineLockObject)
        {
            _obChart.Dispatcher.Invoke(new Action(() =>
            {
                _obChart.AddLineGraph(seriesToAdd, lineColor, 0.5d);
            }));

            //Renew the data series
            _obCurrentRepetitionDataSeries = new DataSeries();
        }
    }
}

private void OnStep(int StepNumber, int HealthyNodes, int MutantNodes)
{
    //Make sure there's a data series to add to
    if (_obCurrentRepetitionDataSeries == null)
    {
        _obCurrentRepetitionDataSeries = new DataSeries();
    }

    lock (lineLockObject)
    {
        //Add the step to the series
        _obCurrentRepetitionDataSeries.Add(new DataPoint() { Number = MutantNodes, Step = StepNumber });
    }
}