线程问题

当使用窗口小部件工具箱时,了解用于阅读和调度平台图形用户界面事件的底层线程模型是很重要的。当在应用程序的代码中使用 Java 线程时,用户界面线程的实现将影响应用程序必须遵循的规则。

本机事件调度

在任何图形用户界面应用程序下面,不管它的语言或用户界面工具箱是什么,操作系统平台都会检测图形用户界面事件,并将它们放在应用程序事件队列中。尽管在不同的操作系统平台上机制稍微有些不同,但是基础是相似的。当用户单击鼠标、输入字符或者对窗口的外观进行处理时,操作系统将生成应用程序图形用户界面事件,例如,鼠标单击、击键或者窗口绘制事件。它确定哪个窗口和应用程序应当接收每个事件,并将它放置在应用程序的事件队列中。

任何窗口化的图形用户界面应用程序的底层结构都是事件循环。应用程序进行初始化,然后启动循环,它只从队列中阅读图形用户界面事件,并相应地作出反应。在处理其中一个事件时完成的任何工作必须快速地进行,以便让图形用户界面系统可以响应用户。

应该在单独的线程中执行由用户界面事件触发的长时间操作,以便允许事件循环线程快速返回,并从应用程序的队列中访存下一个事件。然而,必须利用显式锁定和序列化来控制从其它线程访问窗口小部件和平台 API。未能遵循规则的应用程序可能会导致操作系统调用失败,更糟糕的是,可能会锁定整个图形用户界面系统。

SWT用户界面线程

SWT 遵循平台直接支持的线程模型。应用程序在它的主线程中运行事件循环,并直接从此线程中调度事件。用户界面线程就是在其中创建 Display 的线程。所有其它窗口小部件都必须在用户界面线程中创建。

由于所有事件代码都是从应用程序的用户界面线程中触发的,因此,处理事件的应用程序代码可以自由地访问窗口小部件,并且不需要任何特殊技术就可以进行图形调用。然而,当响应某事件而执行长时间运行的操作时,应用程序负责创建计算线程的分支。

注意,对于所有来自非用户界面线程的调用,而这些调用又必须来自用户界面线程,SWT 将触发 SWTException

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 the user has done
      // something to dispose our window.
      while (!shell.isDisposed ()) {
         if (!display.readAndDispatch())
            display.sleep();
      }
      display.dispose ();
   }

一旦创建了窗口小部件并且打开了 shell,应用程序就会读取和调度操作系统队列中的事件,直到除去 shell 窗口为止。如果队列中没有事件可用,则说明显示进入睡眠状态,以使其它应用程序有机会运行。

SWT 提供了特殊的访问方法来从后台线程中调用窗口小部件和图形代码。

执行非用户界面线程中的代码

希望从非用户界面线程中调用用户界面的应用程序必须提供调用用户界面代码的可运行程序Display 类中的 syncExec(Runnable)asyncExec(Runnable) 方法用来在事件循环期间在用户界面线程中执行这些可运行程序。

以下代码段演示了使用这些方法的模式:

   // 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 中检查窗口小部件是否已被除去。在用户界面线程中,由于在调用 asyncExec 与执行 runnable 之间会执行其它操作,所以无法保证窗口小部件在 runnable 执行时所处的状态。

工作台和线程

当您从头实现 SWT 应用程序时,线程规则是很清楚的,原因是由您控制事件循环的创建以及将应用程序中的计算线程分支的决定。

在向工作台添加插件代码时,情况会略微复杂。可以将下列规则视为使用平台用户界面类时的“约定规则”,尽管随发行版的不同这些规则也会有例外情况: