java - GUI 花费的时间比计算的时间长并且减慢了整个过程

标签 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.");
            }
        }
    }
}

(附加线程仅“计算”步骤并将其发送到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?

最佳答案

我使用的一种方法是使用 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 线程可以访问的数据。

关于java - GUI 花费的时间比计算的时间长并且减慢了整个过程,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46783365/

相关文章:

java - maven-assemble-plugin 被忽略?

java - 给定 N 个点中包含 K 个点的最小面积的正方形

android - Android中进程、 Activity 、线程和任务之间的区别

java - 如果 Eclipse SWT 中未填充所有字段,如何限制用户转到下一页

java - 在 SWT 浏览器中控制图像的来源

java - 自动添加多个 "sections"到 list ?

java - 运行时具有多个数据源的 Spring Batch

c++ - 递归 pthread 生成 - 堆栈位置

类中的 Python 装饰器

java - JScrollPane 中的 JTextPane 对鼠标滚轮没有反应