在SWT程序中,如何启动能够在不阻塞的情况下更新UI的线程

时间:2014-01-23 19:40:33

标签: java multithreading swt

我有一个工作的Java应用程序(大约10k loc),它作为命令行程序启动。几个月前,我“强化”它是一个非常简单的无线SWT应用程序,它增加了很少,但是朝着我现在想做的一步迈进了一步。

我正在尝试调用在线程中运行的worker代码。我在网上看了很多推荐,并提出了一个原型:

package myapp;

import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Point;
import org.eclipse.swt.layout.FillLayout;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.swt.widgets.Text;

public class Console {
    Display display;
    Shell shell;
    Text text;

    private void process() {
        display = new Display();
        shell = new Shell(display);
        shell.setLayout(new FillLayout());
        shell.setLocation(new Point(80, 80));

        text = new Text(shell, SWT.MULTI | SWT.V_SCROLL | SWT.READ_ONLY);

        shell.open();

        Display.getDefault().asyncExec(new Runnable() {
            public void run() {
                new Worker(text).work();
            }
        });

        while (!shell.isDisposed()) {
            if (!display.readAndDispatch())
                display.sleep();
        }

        display.dispose();
    }

    class Worker {
        Text text;

        public Worker(Text t) {
            text = t;
        };

        void work() {
            for (int i = 0; i < 30; i++) {
                text.append("hello " + i + "\n");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    public static void main(String[] args) {
        new Console().process();
    }
}

Worker类代表我的大部分应用程序,

当我运行它时,输出行开始每秒出现,如预期的那样,但是当我尝试以任何方式与窗口交互时(例如移动它),首先窗口没有响应,但是输出停止显示,窗口是可移动的,窗口中没有任何进一步显示,直到处理结束,剩下的所有内容一次全部显示,最终结果显示正确。

如果我不改动窗户,它会正确显示。

我做错了什么?

3 个答案:

答案 0 :(得分:3)

您需要在自己的线程中启动您的worker。一旦得到结果,您可以通过从工作线程调用display.asyncExec将其插入到您的gui中。

这里有一段类似的代码: Snippet7

在这个Snippet中,他们通过syncExec来执行它,它会阻止你的worker直到display-thread执行runnable。这可以更容易同步。或者参见here

答案 1 :(得分:1)

这看起来很麻烦。 Worker睡觉了。

Display.getDefault().asyncExec(new Runnable() {
        public void run() {
            new Worker(text).work();
        }
    });

在我看来,您正在创建一个新的Worker,它看起来像调用Thread.sleep() Block线程。在这种情况下,它是你的GUI线程,因为它是从它内部执行的!

通过删除有问题的Thread.sleep()来解决此问题。我尝试在生产代码中避开Thread.sleep()

为了保持时间安排,您可以尝试将asyncExec移至Worker(未经测试)

class Worker {
    Text text;

    public Worker(Text t) {
        text = t;
    };

    void work() {
        for (int i = 0; i < 30; i++) {
            Display.getDefault().asyncExec(new Runnable() {
                public void run() {
                    text.append("hello " + i + "\n");
                }
            });
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

答案 2 :(得分:0)

您通过在UI线程上调用sleep()来阻止UI。

如果您想定期更新用户界面,请在Display上使用以下方法

org.eclipse.swt.widgets.Display.timerExec(int, Runnable)