6

私はこのデータフローを大まかに持っています:

DataGenerator -> DataFormatter -> UI

DataGeneratorは、データを迅速に生成するものです。DataFormatterは、表示目的でフォーマットするものです。UIはSwing要素の集まりにすぎません。

DataGeneratorを次のようにしたいと思います。

class DataGenerator
{
   final private PropertyChangeSupport pcs;
   ...
   public void addPropertyChangeListener(PropertyChangeListener pcl) {
     this.pcs.addPropertyChangeListener(pcl); 
   }
   public void removePropertyChangeListener(PropertyChangeListener pcl) {
     this.pcs.removePropertyChangeListener(pcl);
   }
}

this.pcs.firePropertyChange(...)データジェネレータに新しいデータがあるときはいつでも電話してください。そうすれば、変更をDataFormatterにプッシュしてからUIにプッシュする責任がdataGenerator.addPropertyListener(listener)ある場所で実行できます。listener

このアプローチの問題は、1秒あたり数千のdataGeneratorの変更があり(状況に応じて1秒あたり10,000〜60,000の間)、UI用にフォーマットするための計算コストが十分に高いため、不必要な負荷がかかることです。 CPU; 私が視覚的に気にするのは、1秒あたり最大10〜20回の変更だけです。

同様のアプローチを使用する方法はありますが、DataFormatterに到達する前に変更イベントを合体させますか?1つのトピックで複数の更新イベントを受け取った場合は、最新のイベントを表示するだけで、前のイベントをすべてスキップできます。

4

5 に答える 5

5

2つのアイデア:

  • 集計PropertyChangeEvent_ 最後のイベントが50ms以上(または適切と思われる時間)前に発生した場合にのみ、拡張PropertyChangeSupport、上書き、発生します。(実際には、作成を防ぐために、シナリオで使用するすべてのメソッドまたは少なくとも1つのメソッドpublic void firePropertyChange(PropertyChangeEvent evt)を上書きする必要があります。)fire*PropertyChangeEvent
  • イベントベースのアプローチ全体を削除します。1秒あたり60.000イベントは非常に多い数です。この状況では、私は投票します。これはMVPの概念上の変更であり、プレゼンターはMVPがアクティブ状態であり、ポーリングする必要があるかどうかを認識しています。このアプローチでは、何千もの役に立たないイベントを生成しません。データの量に関係なく、1秒あたりの可能な限り高いフレームを表示することもできます。または、プレゼンターを固定レートに設定したり、リフレッシュの合間に一定時間スリープさせたり、他の状況(CPU負荷など)に適応させたりすることもできます。

私は2番目のアプローチをとる傾向があります。

于 2012-02-08T22:17:59.663 に答える
3

DataGeneratorEDTスレッドで多くの非GUI作業を行っているようです。DataGenerator拡張SwingWorkerして、で実装されたバックグラウンドスレッドで作業を行うことをお勧めしますdoInBackground。SwingWorkerはpublish結果をGUIに仲介することができますがprocess、EDTで公開された最後のチャンクを受け取り、GUIを更新するメソッドがあります。

SwingWorkersprocessメソッドはチャンクを合体publishedさせるため、公開された中間結果ごとに1回実行されることはありません。

EDTの最後の結果のみを気にする場合は、このコードを使用できます。これは、リストの最後のチャンクのみを気にします。

 @Override
 protected void process(List<Integer> chunks) {

     // get the *last* chunk, skip the others
     doSomethingWith( chunks.get(chunks.size() - 1) );
 }

SwingWorker:中間結果のあるタスクの詳細をご覧ください。

于 2012-02-08T22:42:53.990 に答える
1

サイズ1のを使用して、関数ArrayBlockingQueueを使用してデータをプッシュすることができoffer()ます(つまり、キューがいっぱいの場合は何もしません)

次にScheduledThreadPoolExecutor、キューを定期的にポーリングするを作成します。

このようにして、生成と表示の間の結合を緩めます。

ジェネレーター->キュー->フォーマット/表示

于 2012-02-08T22:25:24.663 に答える
1

もう1つの可能性は、リスナーをジェネレーターに追加することですが、変更に直接反応する代わりに、タイマーを開始するだけです。したがって、リスナーは次のようになります(IDEを起動したり、正確なメソッドシグネチャを検索したりするのが面倒なので、一種の擬似コードで)

Timer timer = new Timer( 100, new ActionListener(){//update the UI in this listener};

public void propertyChange( PropertyChangeEvent event ){
 if ( timer.isRunning() ){
   timer.restart();
 } else {
   timer.start();
 }
}

これは、データジェネレーターが常にデータを生成し続ける場合、または中間更新が必要な場合を除いて機能します。その場合は、timer.restart()呼び出しを削除するか、このスレッドの他の提案(ポーリングメカニズム、またはSwingWorker)を選択することができます。

于 2012-02-08T23:57:33.067 に答える
0

ブロッキングフラグとタイマーを備えた独自の小さな変更リスナーを作成する場合は、次のことができます。

syncronized onChangeRequest() {
    if (flag) {
      flag = false;
      startTimer();
    }
}

timerEvent() {
    notify all your listeners;
}

実際に使用できる優れたブロッキング同時実行フラグがあると思いますが、それが何と呼ばれているのかを一生思い出せません。

于 2012-02-08T22:08:53.893 に答える