ウィジェット・ツールキットを使用している場合は、 プラットフォーム GUI イベントの読み取りおよびディスパッチに使用される基本的なスレッド・モデルを理解することが重要です。 UI スレッドをインプリメントすると、 これらのコードで Java スレッドを使用するときにアプリケーションが従う必要がある規則に影響があります。
GUI アプリケーションでは、言語または UI ツールキットに関係なく、OS プラットフォームは GUI イベントを検出し、 それらをアプリケーション・イベント・キューに格納します。 OS プラットフォームごとに仕組みは微妙に異なりますが、基本は同じです。 ユーザーがマウスをクリックしたり、文字を入力したり、ウィンドウを表示すると、 OS はマウス・クリック、キー・ストローク、ウィンドウ・ペイント・イベントなどのアプリケーション GUI イベントを生成します。 OS はイベントを受け取るウィンドウ (およびアプリケーション) を判別して、 アプリケーションのイベント・キューにイベントを格納します。
すべてのウィンドウ表示 GUI アプリケーションの基本構造はイベント・ループです。 アプリケーションは、キューからの GUI イベントの読み取りおよび適切な応答のみを実行するループを初期化して、始動します。 これらのイベントの 1 つが処理されている間に実行される作業は、 GUI システムがユーザーにすぐに応答できるように、短時間で発生する必要があります。
UI イベントによってトリガーされる長時間の処理は、イベント・ループ・スレッドがすぐに戻り、 アプリケーション・キューから次のイベントを取り出すことができるように、別のスレッド内で実行する必要があります。 ただし、他のスレッドからウィジェットおよびプラットフォーム API にアクセスする場合は、 明示ロックおよびシリアライゼーションによってコントロールする必要があります。 アプリケーションがこれらの規則に従わない場合は、OS 呼び出しに失敗したり、GUI システム全体がロックされることがあります。
C を使用するネイティブ GUI のプログラマーは、 プラットフォーム・イベント・ループの取り扱いに関する設計上の考慮事項に精通しています。 ただし、より高レベルな多くの Java ウィジェット・ツールキットでは、 多くの場合、プラットフォーム・イベント・ループを隠すことによって、アプリケーション開発者を UI のスレッド化問題から保護するように設計されています。
この設計のために、通常は、専用のツールキット UI をセットアップして、 イベント・ループから読み取りやディスパッチを行ったり、 別のスレッドで動作中のアプリケーションで処理されている内部キューにイベントを送付します。 これにより、ツールキットはオペレーティング・システムに十分な時間をかけて応答することができ、 イベントの処理に関するアプリケーションのタイミングが制限されません。 この場合でも、アプリケーションでは、 アプリケーションのスレッドから UI コードにアクセスするための専用のロック技術を使用する必要がありますが、 すべてのアプリケーション・コードは非 UI スレッドで実行されるため、コード全体で一貫性が保持されます。
アプリケーションを UI のスレッド化問題から "保護" する機能は魅力的に見えますが、 実際には多くの問題の原因となります。
GUI イベントのタイミングが Java スレッドのインプリメンテーションおよびアプリケーションのパフォーマンスに依存する場合は、 問題のデバッグおよび診断が困難になります。
最新の GUI プラットフォームは、イベント・キューを使用してさまざまな最適化を実行します。 一般的な最適化は、キュー内の複数のペイント・イベントを縮小することです。 ウィンドウの一部を再描画する必要が生じるごとに、重なっていないかどうか、 またはディスパッチされていない冗長ペイント・イベントがあるかどうか、キューをチェックすることができます。 これらのイベントを 1 つのペイント・イベントにマージして、アプリケーションのペイント・コードのフリッカーを抑え、 頻繁に実行されないようにすることができます。 ウィジェット・ツールキットがキューからイベントを短時間でプルして、内部キューに送付する場合は、 この最適化は効果がありません。
スレッド化モデルに関する開発者の認識が変わると、 他の言語およびツールキットでネイティブ GUI システムをプログラミングした経験のあるプログラマーに混乱をもたらします。
SWT はプラットフォームで直接サポートされているスレッド化モデルに準拠します。 アプリケーション・プログラムはメイン・スレッドでイベント・ループを実行し、 このスレッドから直接イベントをディスパッチします。 これがアプリケーションの "UI スレッド" です。
注: 技術的には、UI スレッドは Display を作成するスレッドです。 また、実際は、UI スレッドはイベント・ループを実行してウィジェットを作成するスレッドです。
すべてのイベント・コードはアプリケーションの UI スレッドからトリガーされるため、 イベントを処理するアプリケーション・コードはウィジェットに自由にアクセスして、 特殊な技術を使わずにグラフィックス呼び出しを行うことができます。 ただし、イベントに応答するのに長時間の処理を実行する場合、アプリケーションは計算スレッドを fork する役割を担います。
注: UI スレッドから実行されるはずの呼び出しが非 UI スレッドから実行された場合、 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();
}
ウィジェットが作成されて、シェルが開くと、シェル・ウィンドウが廃棄されるまで、 アプリケーションは OS キューからイベントを読み取ってディスパッチします。 キュー内に使用可能なイベントがない場合は、 スリープして他のアプリケーションに実行の機会を与えるようにディスプレイに伝えます。
注: SWT アプリケーションの最も一般的なスレッド化モデルでは、単一の UI スレッドを実行して、 計算スレッド内で長時間の処理を実行します。 ただし、SWT では開発者は必ずしもこのモデルに従う必要はありません。 アプリケーションは、各スレッド内でイベント・ループを 1 つずつ実行して、複数の UI スレッドを実行することができます。
SWT は特殊なアクセス方法を使用して、 バックグラウンド・スレッドからウィジェットおよびグラフィックス・コードを呼び出します。
非 UI スレッドから UI コードを呼び出す必要があるアプリケーションには、UI コードを呼び出す Runnable が必要です。 Display クラスの syncExec(Runnable) および asyncExec(Runnable) メソッドは、 適切な時期に UI スレッド内でこれらの実行可能コードを実行するために使用されます。
次のコードの断片は、これらのメソッドの使用パターンを示します。
// do time intensive computations
...
// now update the UI.We don't depend on the result,
// so use async.
Display.getCurrent().asyncExec(new Runnable() {
public void run() {
myWindow.redraw();
}
});
// now do more computations
...
SWT アプリケーションをゼロからインプリメントする場合は、 イベント・ループの作成や、アプリケーションの計算スレッドを fork する判断をコントロールできるため、 スレッド化規則は非常にわかりやすくなります。
ワークベンチにプラグイン・コードを追加している場合は、どのような規則が適用されるでしょうか。 幸運なことに、JFace またはワークベンチ・コード内にスレッド化 "マジック" は潜んでいません。 規則は明白です。