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