C# wpfui更新线程问题

C# wpfui更新线程问题,c#,wpf,multithreading,asynchronous,C#,Wpf,Multithreading,Asynchronous,更新 我已经解决了这个问题。我发现我没有正确同步对线条和图表对象的访问。我没有在处理程序期间锁定它们,而是只在使用它们的那一刻锁定它们。我认为动态数据显示库中的某些东西使用了延迟执行,并试图在我没有预料到的情况下访问其中一个对象 TL;DR:正确地同步锁 我目前正在开发一个GUI,使用WPF来可视化某个类库中的算法。我在GUI中使用的所有控件都是标准WPF或来自Microsoft库 我得到了一个非常常见的“调用线程无法访问此对象,因为另一个线程拥有它”错误。我已经做了大量的搜索,但自己找不到解

更新

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

TL;DR:正确地同步锁


我目前正在开发一个GUI,使用WPF来可视化某个类库中的算法。我在GUI中使用的所有控件都是标准WPF或来自Microsoft库

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

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


摘要窗口是主窗口的子窗口,在模拟运行和实时显示数据之前打开

模拟器发布许多事件,这些事件都是异步处理的(在内部,它使用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);
        }
    }
}
公共类MainWindowModel
{
//...
公共异步void RunSimulation()
{
WindowService.OpenWindowFor(SimSummaryVM);
等待模拟器。运行();
}
}
公共类模拟器摘要视图模型
{
//...
公共模拟器摘要视图模型()
{
添加(新RealTimeChart());
}
//...
}
公共类实时图表:iVisualization
{
// ...
专用图表绘图仪_Chart;//来自动态数据显示库
//处理将信息添加到当前行的操作
模拟者Stephandler(参数a)上的私有无效
{ 
/*在这里,当添加数据点时会发生异常,
而另一个处理程序正在向图表中添加一行。
我曾尝试向图表和线条对象添加锁,但没有效果
*/ 
}
//处理将当前行添加到图表的操作
模拟重复完成时的私有无效(参数a)
{
//lineToAdd是一个可枚举的数据源。
//DataPoint是一个具有两个基本属性的简单类。
_Chart.Dispatcher.Invoke(新操作(()=>
{
_图表。添加线条图(线条添加);
}));
}
}
公共类摘要窗口:窗口
{
// ...
公共摘要窗口()
{
// ...
foreach(可视模型中的可视化可视化)
{
Vis.Draw(这个VisCanvas);
}
}
}

我假设OnSimulatorRepeationComplete是从另一个线程调用的,在该线程中调用图表

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

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

}));
所以它变成了这样:

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

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


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

我使用的图表控件推迟了
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 });
    }
}
[STAThread]
私有void OnRepetitionComplete(Evolve.Core.Simulation.RepetitionResult)
{
//将数据系列转换为可用于图表的内容
EnumerableDataSource seriesToAdd=新的EnumerableDataSource(_obCurrentRepetitionDataSeries);
//将中的数据点特性转换为图形上的X&Y坐标
seriesToAdd.SetXYMapping(DP=>newsystem.Windows.Point(DP.Step,DP.Number));
//获取线条颜色并将线条添加到图表中
System.Windows.Media.Color lineColor=GetLineColor(Result.ReasonForCompletion);
锁定(chartLockObject)
{
锁定(lineLockObject)
{
_obChart.Dispatcher.Invoke(新操作(()=>
{
_obChart.AddLineGraph(系列添加,线条颜色,0.5d);
}));
//更新数据系列
_obCurrentRepetitionDataSeries=新数据系列();
}
}
}
私有void OnStep(int StepNumber、int HealthyNodes、int MutantNodes)
{
//确保有要添加的数据系列
if(_obCurrentRepetitionDataSeries==null)
{
_obCurrentRepetitionDataSeries=新数据系列();
}
锁定(lineLockObject)
{
//将步骤添加到系列中
_添加(新数据点(){Number=MutantNodes,Step=StepNumber});
}
}

要添加的行的类型是什么?它是一个可枚举的数据源,其中数据点是具有两个基本属性的某个内部类。我会把它添加到我的问题中。你能更具体地说明一下《模拟人》的内容吗?谢谢你的询问,看看我的更新:)我也会发布一个答案。