複雑な計算には時間がかかります。いくつかの入力値では、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をどう扱うのですか?
あなたは 'invokeLater()'を使ってGUIアップデートを処理していると仮定しますか? – Kayaman
@Kayaman 'invokeLater()'はスイングですね。私たちはSWTを使用しています。 –
私の記憶が私にはうまくいけば、スウィングはイベントを合体させてスループットを向上させる能力を持っています。私はSWTに類似したものがあると思います。あなたの例はあまりにも抽象的です。使用している実際のテクノロジーを使用して[MCVE](https://stackoverflow.com/help/mcve)を作成し、それを解決しようとします。 – Kayaman