GUI 比计算花费的时间更长,并且会减慢整个过程
GUI Takes Longer Than Calculation And Slows Entire Process
我们有一个需要可变时间的复杂计算。对于一些输入值,一秒钟内可以完成数千步 - 对于其他输入值,一步需要几秒钟。
完全正确,所以我们只想通知用户进度。问题在于,在前一种情况下,更新 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
仅 "calculates" 步骤并将其发送到 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 框架 API,因为那是我们正在使用的。)
那么...如何处理更新时间比计算时间长并因此减慢应用程序速度的 GUI?
不知道我是否理解正确,但您似乎一直在更新 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 步更新一次。
为什么update方法中有sleep?
希望能帮到你。
我使用的一种方法是使用 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 线程可以访问的数据。
我们有一个需要可变时间的复杂计算。对于一些输入值,一秒钟内可以完成数千步 - 对于其他输入值,一步需要几秒钟。
完全正确,所以我们只想通知用户进度。问题在于,在前一种情况下,更新 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
仅 "calculates" 步骤并将其发送到 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 框架 API,因为那是我们正在使用的。)
那么...如何处理更新时间比计算时间长并因此减慢应用程序速度的 GUI?
不知道我是否理解正确,但您似乎一直在更新 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 步更新一次。
为什么update方法中有sleep?
希望能帮到你。
我使用的一种方法是使用 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 线程可以访问的数据。