4

私のチームと私は、(Infragisticsによる)複数の同時XamDataChartコントロールを表示するWPFアプリケーションを開発しています。各グラフは、最大200万ポイントを含むことができる異なるObservableCollectionにバインドされています。DispatcherTimerは、チャートごとに、コレクションに追加される新しいアイテムを定期的に取得します(100ミリ秒ごとに最大1000)。新しいアイテムが来るたびに、それらはコレクションの「テール」に追加され、同じ量が「ヘッド」から削除されるため、コレクション内のアイテムの量は時間の経過とともに一定に保たれます。

私たちが直面している問題は、コレクションはメインスレッドによってのみ変更できるため、追加/削除操作によってGUIがフリーズすることです。多くのアプローチ(BackgroundWorker、Application.Current.DispatcherとDispatcherPriority.Background、Task.Factoryなど)を試しましたが、どれも問題を解決していないようで、GUIがフリーズし続けます。

GUIの応答性を維持しながら、大量のバインドされたデータを処理するための最良のアプローチについて教えてください。

更新

1)以下のコメントに示されているように、OnCollectionChangedを抑制しながら、アイテムの追加と削除をすでに試みました。少量のデータで効果があるように見えても、このシナリオでは、このソリューションの利点は実際には観察できません。

2)データは別のスレッドで作成および収集されます。これは長時間実行される操作ですが、速度の低下や無反応は明らかではありません。データがレンダリングのためにチャートコンポーネントに渡されると、アプリケーションがフリーズします。

3)(別のスレッドで)データを生成し、UIにデータを表示するメソッドは次のとおりです。

private void GenerateDataButtonClick(object sender, RoutedEventArgs e)
{
   Task<List<RealTimeDataPoint>> task = Task.Factory.StartNew(() =>           this.RealTimeDataPointGenerator.GenerateData(2000000));
   Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() =>  {                                                                                                    this.DataPoints.Clear();
                                                                                            this.DataPoints.AddRange(task.Result);
                                                                                                 if (!this.StartDataFeedButton.IsEnabled)
                                                                                                     this.StartDataFeedButton.IsEnabled = true;
                                                                                             }));
}

public void DispatcherTimerTick(object sender, EventArgs e)
{
   Task<List<RealTimeDataPoint>> task = Task.Factory.StartNew(() => this.RealTimeDataPointGenerator.GenerateData(1000));
   Application.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, new Action(() => {                                                                                                  this.DataPoints.RemoveRange(0, task.Result.Count);                                                                                                  this.DataPoints.AddRange(task.Result);                                                                                              }));
}

よろしくお願いします、Gianluca

4

2 に答える 2

1

シナリオについて詳しく知らなければ正しい提案を知ることは難しいので、サンプルをInfragisticsフォーラムに投稿し、支援を求めることをお勧めします。まだ行っていない場合は、次のリンクを使用して返信してください。投稿、そして私はそれを見ていきます。

個別の操作として一度に多くのポイントを更新する場合は、個々のイベントではなく、コレクションで1つのイベントのみを発生させると便利です。たとえば、Addを繰り返し呼び出してコレクションのコンテンツ全体を更新する場合は、個々のイベントすべてではなく、1つのResetイベントを送信するのが最適です。しかし、すでにAddRangeを呼び出しているように見えます。これは、チャートに通知を1つだけ送信すると思います。

シリーズが2つだけで、100ミリ秒ごとに1回だけ更新する場合、これによってUIがフリーズすることはないと思いますが、個別のディスパッチャーインタラクションを使用して個別にデータを更新する個別のシリーズが多数ある場合は、実際に発生します。おそらくあなたが意図しているよりもはるかに多くのチャートの更新。

グラフは変更をまとめて更新の数を制限しますが、これはディスパッチャーを使用して行われます。したがって、異なる間隔で更新する15の異なるシリーズがあり、すべてが別々のディスパッチャーサンクである場合、さらに多くの原因が発生します。同じディスパッチャサンク内の複数の系列データソースを更新することによって更新の数を抑制した場合よりも、グラフを更新します。

さらに、上記のコードでCategoryDateTimeXAxisを使用している場合、変更時に現在日付列を並べ替えるという制限に達している可能性があります。これにより、その規模でのパフォーマンスが低下します。この場合、事前に並べ替えられたデータをサポートするために、その軸タイプの機能リクエストを送信することをお勧めします。

データアイテムがINotifyPropertyChangedをサポートしているが、これを使用して値の変更のグラフ​​を通知していない場合は、INotifyPropertyChangedを実装していないアイテムタイプを使用する方がはるかに適切です。このインターフェースを実装するアイテムを送信する場合、チャートは、変更の通知を受け取るためにこれをサブスクライブする必要があると想定します(変更を通知するつもりはない場合があります)。これは問題のようには聞こえないかもしれませんが、頻繁に更新する200万件のレコードがある場合、それは目的のないイベントサブスクリプションの数が多くなります。

私の知る限り、このグラフは文字列インデクサーよりもプロパティバインディングから値を取得する方がはるかに高速なので、MemberPathの点線のプロパティパスではなく、単純なプロパティであることを確認してください。

この情報がお役に立てば幸いです。ただし、問題の原因となる可能性のあるすべてのコンテキストを提供する実行可能なサンプルを使用して、問題を診断する方がはるかに簡単です。

于 2012-07-24T13:49:03.730 に答える
0

私もこの問題に遭遇しました。データを取得するためのコードに時間がかかることが多く、を使用しているにもかかわらず読み込み中にUIがフリーズし、DispatcherPriority.BackgroundWPFがオブジェクトを変更できないため、バックグラウンドスレッドを使用できませんでした。オブジェクトを作成しなかったスレッド。

私がやったことは、実際には両方の方法を使用することでした。データを取得するためのバックグラウンドスレッドと、優先順位Dispatcherにアイテムを追加するためのバックグラウンドスレッドです。ObservableCollectionDispatcherPriority.Background

私のコードは通常次のようになります。

Task<List<MyClass>> task = Task.Factory.StartNew(() => GetData());
App.Current.Dispatcher.BeginInvoke(DispatcherPriority.Background, 
    new Action(delegate() 
    {
        MyObservableCollection.AddRange(task.Result);
    }));

私が過去に使用した別のオプションは、await/ AsyncCTPRefreshasyncのキーワードです。

async void SomeMethod()
{

    Task<List<MyClass>> task = Task.Factory.StartNew(() => GetData());
    MyObservableCollection.AddRange(await task);
}

:このAddRange()メソッドは、拡張するカスタムクラスの一部ObservableCollectionですが、おそらく拡張メソッドとして作成することもできます。

public void AddRange(IEnumerable<T> collection)
{
    foreach (var i in collection)
        Items.Add(i);

    OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}

CollectionChanged最後に1回のリセット呼び出しの代わりに、追加されたアイテムごとに呼び出しを追加することもできますが、追加するデータの量にパフォーマンスの問題が発生する可能性があります。

于 2012-07-23T15:31:22.877 に答える