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


(附加的Thread仅“计算”步骤并将其发送到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 - 如何设置 block 的超时?

java - 由多个线程调用的ImageIO.write是否安全?

iphone - NSLock 实例应该是 "global"吗?

java - 使 SWT StyledText 小部件始终滚动到其末尾

java - SWT-使表格可滚动

java - 在GUI中处理异常时出现问题

java - 退订不会级联回到RxJava中可观察到的基础

.net - 仅使用原子操作的 C++/CLI 中的线程同步

Java SWT - 组内的 ScrolledComposite

java - Java-根据类的名称获取类的对象