2

アップデート

私はこの問題を解決しました。線とグラフのオブジェクトへのアクセスを適切に同期していないことがわかりました。ハンドラーの期間中それらをロックする代わりに、私はそれらが使用された瞬間だけそれらをロックしました。Dynamic Data Displayライブラリ内の何かが、遅延実行を使用していて、予期していなかったときにオブジェクトの1つにアクセスしようとしていたと思います。

TL; DR:ロックを適切に同期します。


私は現在、WPFを使用していくつかのクラスライブラリのアルゴリズムを視覚化するGUIに取り組んでいます。GUI内で使用しているすべてのコントロールは、標準のWPFまたはMicrosoft DynamicDataDisplayライブラリからのものです。

非常に一般的な「別のスレッドがオブジェクトを所有しているため、呼び出し元のスレッドはこのオブジェクトにアクセスできません」というエラーが発生します。私はかなりの量の検索を行いましたが、これに対する解決策を自分で見つけることができません。

今のところ、スレッドがチャートを所有していない場合にチャートに線を追加する方法について少し混乱しています。このサイトの他の多くの回答が示唆しているように、ディスパッチャを使用してみましたが、これは解決されていません。


サマリーウィンドウは、シミュレーションが実行される前に開き、データをリアルタイムで表示するメインウィンドウの子ウィンドウとなることを目的としています。

シミュレーターは、すべて非同期で処理される多数のイベントを公開します(内部的には、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);
        }
    }
}
4

2 に答える 2

2

OnSimulatorRepetitionCompleteが別のスレッドから呼び出され、そのスレッドで発生しているチャートを呼び出すと仮定します。

WPFでは、すべてのGUI操作をディスパッチャースレッドで実行する必要があります。メソッドのコンテンツの周りに次のコードを追加します。

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

}));

だからそれはこれになります:

private void OnSimulatorStepHandler(Args a)
{ 
    Dispatcher.BeginInvoke(new Action(() =>
    {
        // Add your code here
    }));
}
于 2013-03-07T14:08:15.637 に答える
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 });
    }
}
于 2013-03-07T14:19:48.493 に答える