GUI比计算更长并且减慢整个过程

时间:2017-10-17 05:56:07

标签: java multithreading swt

我们有一个复杂的计算,需要一个可变的时间。使用一些输入值,可以在一秒内完成一千步 - 其他输入值一步需要几秒钟。

这完全正确,所以我们只想告知用户进度。问题在于,在前一种情况下,更新GUI需要比实际计算更长的时间,因此在完成后,队列中仍有大约10秒的GUI更新事件(在这种情况下,三倍于整个计算的执行时间)

我认为这是一个普遍的问题,所以我将其分解为一个与框架无关的例子:

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&#34;计算&#34;步骤并将其发送到GUI以显示进度。)

因此,让我们考虑Gui的一些实现:

static class NoGui implements Gui {

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

此示例仅在GUI完成时打印。结果是这两行几乎同时打印出来:

Finished calculation.
Finished GUI.

这完全合情合理。 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间隔三秒钟:

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

这就是我在申请中看到的内容。计算结束,但GUI太慢,并且必须在计算完成后执行其事件队列。

我想优化这种行为并想出类似的东西:

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;
        }
    }
}

输出如下四行:

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

这个实现只是忽略了两者之间的事件,因此更快。这是解决我问题的有效方法。

我认为整个用例可能很常见,也许有更优雅的解决方案。甚至是一些标准的Java API来处理它。 (也许是一些SWT / Eclipse Framework API,因为那是我们正在使用的。)

那么......如何处理比计算需要更长时间更新的GUI,从而减慢应用程序的速度?

2 个答案:

答案 0 :(得分:2)

我使用的一种方法是使用UI线程中可运行的计时器轮询后台线程。请使用Display.timerExec

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

     // TODO update UI from background thread details

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

后台线程不执行任何asyncExec调用,它只维护UI线程可以访问的数据。

答案 1 :(得分:0)

不知道我是否做得对,但似乎你不断更新GUI。尝试添加类似计数器的东西,以确定何时应该更新gui。或者,如果不需要看到所有步骤尝试类似

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();
    }
}

}

这应该每5步更新一次。

为什么在更新方法中有睡眠?

希望我能帮助你。