ウィジェット・ツールキットを使用している場合は、 プラットフォーム 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 では開発者は必ずしもこのモデルに従う必要はありません。 アプリケーションは、各スレッド内で個別のイベント・ループを持つ複数の 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.getDefault ().asyncExec (new Runnable () { public void run() { myWindow.redraw(); } }); // now do more computations ...
SWT アプリケーションを最初からインプリメントする場合は、 イベント・ループの作成や、アプリケーションの計算スレッドを fork する判断を制御できるため、 スレッド化規則は非常にわかりやすくなります。
ワークベンチにプラグイン・コードを追加している場合は、JFace またはワークベンチ・コード内にスレッド化「マジック」は潜んでいません。 規則は明確です。