2017-10-17 5 views
0

複雑な計算には時間がかかります。いくつかの入力値では、1秒で1000ステップが実行できます。他の入力値では、ステップに数秒かかります。GUIが計算時間が長くなり、プロセス全体が遅くなる

これは完全に正しいので、ユーザーに進捗状況を伝えたいだけです。問題は、前者の場合、GUIの更新には実際の計算よりも時間がかかるため、実行後はまだキューにGUI更新イベントが約10秒間あります(この場合、計算全体の実行時間は3倍になります) 。

私はそれが一般的な問題だと思うので、私は多少の枠組みにとらわれない例にそれを壊した:

public class QueueTest { 

    static final int STEPS = 30; 

    public static void main(String[] args) { 
     final Gui gui = // ... 

     final Display display = Display.getDefault(); 
     final Thread thread = new Thread(() -> { 
      for (int i = 0; i < STEPS; i++) { 
       final int step = i; // calculate something etc. 
       gui.updateLater(display, step); 
      } 
      System.out.println("Finished calculation."); 
     }); 
     thread.start(); 

     while (true) { 
      if (!display.readAndDispatch()) { 
       display.sleep(); 
      } 
     } 
    } 

    interface Gui { 

     default void updateLater(Display display, int step) { 
      display.asyncExec(() -> update(step)); 
     } 

     default void update(int step) { 
      System.out.println("Update " + (step + 1) + "/" + STEPS); 
      if (step == STEPS - 1) { 
       System.out.println("Finished GUI."); 
      } 
     } 
    } 
} 

(追加Threadのみ進行状況を表示する手順を「計算」し、GUIに送信します。)

それではGuiのいくつかの実装を考えてみましょう:

static class NoGui implements Gui { 

    @Override 
    public void update(int step) { 
     if (step == STEPS - 1) { 
      System.out.println("Finished GUI."); 
     } 
    } 
} 

この例は、GUIが終了したときにのみ表示されます。その結果、これらの2行はほぼ同時に印刷されます。

Finished calculation. 
Finished GUI. 

これは完全に合理的です。 GUIイベントはすばやく完了します。今度はゆっくりとそれらを作ってみましょう:私は、我々のアプリケーションで見ているものだ

Finished calculation. 
Update 1/30 
Update 2/30 
Update 3/30 
... 
Update 30/30 
Finished GUI. 

static class SlowGui implements Gui { 

    @Override 
    public void update(int step) { 
     try { 
      Thread.sleep(100); 
      Gui.super.update(step); 
     } catch (final InterruptedException e) { 
      e.printStackTrace(); 
     } 
    } 
} 

これは、計算の仕上げとGUI離れて3秒で、次のようなものを出力します。計算は終了しますが、GUIが遅すぎるため、計算が完了した後でイベントキューを実行する必要があります。

私はこの動作を最適化したいと、このような何かを思い付いた:この実装は、ちょうど間などでのイベントを無視

Finished calculation. 
Update 1/30 
Update 30/30 
Finished GUI. 

:出力は、次の4つのラインである

static class IgnorantGui extends SlowGui { 

    private boolean inProgress; 
    private Integer nextStep; 

    @Override 
    public void updateLater(Display display, int step) { 
     if (this.inProgress) { 
      this.nextStep = Integer.valueOf(step); 
     } else { 
      this.inProgress = true; 
      super.updateLater(display, step); 
     } 
    } 

    @Override 
    public void update(int step) { 
     try { 
      Integer currentStep = Integer.valueOf(step); 
      do { 
       super.update(currentStep.intValue()); 
       currentStep = this.nextStep; 
       this.nextStep = null; 
      } while (currentStep != null); 
     } finally { 
      this.inProgress = false; 
     } 
    } 
} 

はるかに速いです。これは私の問題に対する有効な解決策です。

私は、このユースケース全体が共通しているかもしれないと思うし、もっと洗練されたソリューションがあるかもしれません。またはそれを処理するための標準的なJava APIもあります。

だから、計算よりも更新に時間がかかり、アプリケーションが遅くなるGUIをどう扱うのですか?

+0

あなたは 'invokeLater()'を使ってGUIアップデートを処理していると仮定しますか? – Kayaman

+0

@Kayaman 'invokeLater()'はスイングですね。私たちはSWTを使用しています。 –

+0

私の記憶が私にはうまくいけば、スウィングはイベントを合体させてスループットを向上させる能力を持っています。私はSWTに類似したものがあると思います。あなたの例はあまりにも抽象的です。使用している実際のテクノロジーを使用して[MCVE](https://stackoverflow.com/help/mcve)を作成し、それを解決しようとします。 – Kayaman

答えて

2

私が使用する1つの方法は、UIスレッドでタイマーrunnableを使用してバックグラウンドスレッドをポーリングすることです。このためDisplay.timerExecを使用します。

display.timerExec(100, new Runnable() { 
    @Override 
    public void run() { 

    // TODO update UI from background thread details 

    // Run again 
    display.timerExec(100, this); 
    } 
}); 

バックグラウンドスレッドは、それだけでUIスレッドがアクセスできるデータを維持する任意のasyncExec呼び出しを行いません。

0

私はそれが正しいかどうかわかりませんが、あなたは継続的にGUIを更新しているようです。 guiをいつ更新するかを決定するカウンタのようなものを追加してください。それとも、それはイマイチ場合は、すべての手順は、これはすべての5つのステップを更新する必要があります

static class SlowGui implements Gui { 

@Override 
public void update(int step) { 
    try { 
     if(step%5==0){ 
      Gui.super.update(step); 
     } 
    } catch (final InterruptedException e) { 
     e.printStackTrace(); 
    } 
} 

}

ような何かをしようと見ることが必要。

なぜ更新方法でスリープ状態になっていますか?

私はあなたを助けることができます。

+0

問題は、計算がGUIよりも遅い場合は、すべての手順で更新するのに問題はないということです。どのような場合があるかは事前にわかっていません。 ...そして、スリープ状態は、遅いGUIをシミュレートするためにあります。 –

+0

あなたのcalcメソッドにあなたのGUIへの参照を与えるようにしてください。次に、計算方法の中からGUIを呼び出したり、GUIを変更したりします。計算が遅いか速いかは関係ありません。次の計算はguiの準備ができた後に開始されます。時間がかかる場合は、X回ごとに更新してください。 – Cryptor

+0

いいえ。GUIを混在させても計算は不要です。そしてそれは計算をかなり遅くするでしょう。私はむしろGUI消費のイベントをフィルタリングしたいと思う。 –