当做 widget toolkit 方面的工作时,理解底下的线程模型很重要,它用来读取和分发平台的GUI事件。当使用Java线程时,UI线程的实现影响到应用必须遵循的规则。
本地时间分发
操作系统将时间分发给应用程序
SWT UI 线程
UI有自己的独立的线程,Display 里创建。
应用的主线程负责负责处理 event loop,SWT 应用一般都有下面对结构:
public static void main(String[] args){
Display display = new Display();
Shell shell = new Shell(display);
shell.open();
// start the event loop. We stop when th euser has done
// something to dispose our window.
while(!shell.isDisposed()) {
if(!display.readAndDispatch()) {
display.sleep();
}
}
display.dispose();
}
在一个非UI 线程那里执行代码
同步
syncExec(Runnable)
非UI线程需要从 UI 代码那里获得返回值,或者 在返回到线程之前,确保 runnable 运行完成。
异步
asyncExec(Runnable)
在应用程序需要执行一些UI操作,并不依赖操作的完成再继续。
下面是一个使用这些方法的演示:
// do time-intensive computations
...
// now update the UI. We don't depend on the result,
// so use async.
display.asyncExec (new Runnable () {
public void run () {
if (!myWindow.isDisposed())
myWindow.redraw ();
}
});
// now do more computations
...
一个好的练习,使用 asyncExec 在你的 runnable 里面检查 你的 widget 是否已经被销毁。由于在UI线程内部,调用asyncExec和你的runnable的执行时间内,其他的事情可能发生,因此,你不能确认在你的runnable执行期间,你的widget是什么状态。
工作台和线程
当你从底部往上,实现一个 SWT 应用,由于你控制了event loop 的创建和fork计算线程的决定,线程的规则就变得非常明确。
当你将插件的代码发布到工作台,事情就有一点复杂了。当使用平台UI类s,下面的规则可以被考虑成“承诺的规则“,虽然随着版本的更新,这些规则会有一些异常:
• 一般情况下,你增加到平台的任何工作台UI扩展,都在工作台的UI线程内执行,除非它们与线程s或者背景工作s都特别相关(例如背景工作进度表示)。
• 如果你从工作台那里接收到一个事件,但不敢保证,它在工作台的UI线程内执行。查阅javadoc里面的特定的类,这个类定了监听器或者事件。如果没有描述线程的相关文档,并且该类明显是一个UI相关的测类,你可以期望这个事件在工作台线程内到达。
• 同样,一个平台UI库不需要考虑线程安全,否则特别给它做文档。注意,大多数的平台UI类从触发事件的调用线程分发监听器。工作台和JFace API的调用不检查调用者是否在UI线程内。如果你调用从一个非UI线程里面调用一个触发事件的方法,你的插件将产生一个问题。这种情况,SWT触发一个SWTException。一般情况下,避免从另一个线程那里调用平台UI代码,除非javaodc特别允许。
• 如果你的插件 fork一个计算线程或者使用一个工作台的job,必须使用 Display 的 asyncExec(Runnable) 或者 syncExec(Runnable)方法(为工作台,JFace或者SWT调用任意API),除非相关的API特别允许被背景线程call-in。
• 如果你的插件使用JFace的IRunnableContext接口调用一个进度监视器和运行一个操作,它提供一个参数,指明是否为运行的操作fork一个计算线程。
