2013-03-07 131 views
2

UPDATEWPF UI更新线程问题

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

TL; DR:正确同步锁。


我目前正在使用WPF的图形用户界面来可视化某些类库中的算法。我在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); 
     } 
    } 
} 
+1

lineToAdd是什么类型? – sim1 2013-03-07 13:50:28

+0

这是一个EnumerableDataSource 其中数据点是具有两个基本属性的某个内部类。我会将其添加到我的问题。 – AndyBursh 2013-03-07 13:53:54

+1

你可以更具体的OnSimulatorStepHandler的内容? – sim1 2013-03-07 14:04:16

回答

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

我认为OnSimulatorRepetitionComplete从另一个线程调用,进行调用图表中的线程发生。

在WPF中,所有的GUI操作需要在调度线程来完成。在你的方法 周围添加内容验证码:

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

})); 

所以就变成这样:

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