ユーザーインターフェイススレッド

ユーザーインターフェイススレッド  とは、アプリケーションソフトウェアのグラフィカルユーザーインターフェイス  におけるメインスレッドのことを指す。UIスレッドと表記されることもある。本項では、GUIでのマルチスレッドに関するデザインパターンを記載する。ユーザーインターフェイススレッドは、Javaではイベントディスパッチスレッド、Adobe Flashではprimordial workerと呼ばれる。

1. 歴史的経緯
--------

GUIアプリケーションにおいて、応答性を維持するためには、基本的にフレームの描画およびユーザー応答はできるかぎり高いフレームレートで処理しないといけない必要となるフレームレートは、アプリケーションの用途やモニターのリフレッシュレートなどの環境によっても異なる。[[仮想現実。例えば60fps  の場合、1フレームの処理を1/60秒=約16ミリ秒以内に完了する必要がある。しかし、たとえどれほどプロセッサの性能が向上したとしても、I/O処理や画像・動画のデコード、通信接続の確立など、完了までに長時間かかってしまう処理は必ず存在する。そういったものを含めて、すべての処理を1つのスレッドだけで実行すると、長時間かかる処理を実行している間はフレーム描画やユーザー応答の処理ができないため、UIの反応がなくなってしまう。長時間かかる処理の途中に、フレーム描画やユーザー応答に関わるメッセージ処理をときどき挟みながら、長時間かかる処理を少しずつ進める、という方法もあるが、スマートでないうえに適用限界がある。応答性の低下を防ぐためにマルチスレッド化が必要だが、それをどのようにしてソフトウェアの構造設計に持ち込むか、ということに関して、歴史的には1980年代から色々と議論があった。

例えば、JavaのAWTでは、1996年の最初の時点では、単純にスレッド間でデータ共有型のマルチスレッドになっていた。しかし、データ共有するには、ロックをかけないといけないが、親コンポーネントから子コンポーネントを呼んだり、コールバックで子から親を呼んだり、アプリケーションからGUIライブラリを呼んだり、GUIライブラリからアプリケーションをコールバックしたりと、双方向に呼び出すことが多く、異なるスレッド間で双方向に呼び合うときは、ロックの順番に注意を払う必要がある。これはソフトウェアが非常に複雑になる原因となってしまう。また、ロック順序のミスが引き起こすデッドロックは常にではなくたまに発生したりすることの多いバグであり、バグ取りが大変になるという問題があった。

そこで、1997年のJavaのSwingからは、UIの操作は全てメインのUIスレッドであるイベントディスパッチスレッドから操作しなくてはならない、というルールを設けた。そして、2006年の Java 6 から、UIスレッドで重い処理をすることを避けるために、ワーカーデザインパターンを採用した name=javax.swing.SwingWorker を搭載した。

現在では、多くのUIライブラリが、UIスレッドに操作を限定することと、ワーカーデザインパターンの組み合わせを採用している。

GUIの描画に関しては、UIスレッドが実行するケースもあれば、UIスレッドとは別の専用レンダリングスレッドが実行するケースもある。

アプリケーションのメインスレッドは基本的にUIスレッドと同一・等価だが、特殊なケースとして、メインスレッドとUIスレッドが異なるケースもある。

2. UIスレッドへの委譲
-------------

UIスレッドではないサブスレッドでの処理の進捗や完了を画面表示する場合などは、処理の途中でユーザーインターフェイス要素の操作が必要となる。そういった場面では、サブスレッドからUIスレッドに操作を依頼することが、まず必要である。

2.1. Java
---------

Javaの場合は、以下の方法で、UIスレッドに委譲できる。J2SE 1.3以降ではどちらもEventQueueのメソッドが呼び出される実装となっている。

同期処理 - member=invokeAndWait,
非同期処理 - member=invokeLater, 同期処理は、処理が完了するまで待つ。

委譲される処理は、name=java.lang.Runnableインターフェイスのrunメソッドの実装として記述する。

2.2. 他のライブラリ
------------

.NET Framework/.NET Core
Windows Forms: System.Windows.Forms.Control.Invoke, System.Windows.Forms.Control.BeginInvoke
WPF: System.Windows.Threading.Dispatcher.Invoke, System.Windows.Threading.Dispatcher.BeginInvoke
WPF 4.5: System.Windows.Threading.Dispatcher.InvokeAsync
Windowsランタイム/Windows UI Library: Windows.UI.Core.CoreDispatcher.RunAsync
Android: Activity.runOnUiThread, View.post, View.postDelayed.NETでは処理の委譲にデリゲートが使用されることが多い。

3. ワーカーデザインパターン
---------------

3.1. Java
---------

javax.swing.SwingWorker を継承して、実装できる。A→B→Cと処理が進むとき、Bが重い処理とすると、Bを SwingWorker.doInBackground で、Cを SwingWorker.done で処理する。

実装例： SwingWorker<Document, Void> worker = new SwingWorker<Document, Void> { @Override public Document doInBackground throws IOException { return loadXML; // 重い処理 }

@Override public void done { try { Document doc = get; display; } catch  { ex.printStackTrace; } } }; worker.execute;<WtXmlEndTag /> doInBackgound の戻り値を done の SwingWorker.get で取り出せる。done はUIスレッドで動作するが、doInBackgound はUIスレッドとは別のスレッドで動作する。上記例では、done の中で読み込んだ XML の表示を行う。

3.2. Groovy
-----------

上記の例を Groovy で書く場合は、groovy.swing.SwingBuilder に doLater, doOutside, edt があり、下記のようによりシンプルに書ける。 doOutside { def doc = loadXML // 重い処理 edt { display } }

3.3. 他のライブラリ
------------

System.ComponentModel.BackgroundWorker - .NET Framework
タスク並列ライブラリ  - .NET Framework
並列パターンライブラリ  - Visual C++/Windowsランタイム
flash.system.Worker - Adobe Flash
android.os.AsyncTask - Android同一インスタンスのワーカーを2回以上実行したときに、それは、逐次処理するべきなのか、並列処理するべきなのかという議論があり、UIライブラリによって様々であるが、Androidにおいては、2009年9月のAndroid 1.6までは逐次処理、2009年11月のAndroid 2.0からは並列処理、2011年2月のAndroid 3.0からはデフォルトは逐次処理と、色々と変遷した。並列処理にすると使うときにバグを生みやすくなると言うのが、逐次処理に戻した理由である。なお、ワーカー自体が逐次処理であっても、複数のワーカーインスタンスを作り、別々に実行すれば並列に処理できる。

Android 11以降はandroid.os.AsyncTaskは非推奨  となり、name=java.util.concurrentまたはKotlinコンカレンシーユーティリティの使用が推奨されている。

.NET言語では、TPLとコルーチンをバックエンドに利用した非同期処理の糖衣構文async/awaitも用意されている。async/awaitを使うことで、重い処理はいったんサブスレッドに委譲しておき、その処理の完了を受けて後続処理を再開するようなコードを簡潔に記述できる。

4. タイマー
-------

UIスレッドで呼び出されるタイマーも多くのライブラリで備わっている。

javax.swing.Timer - Java
System.Windows.Forms.Timer - .NET Framework/Windows Forms
System.Windows.Threading.DispatcherTimer - .NET Framework/WPF
Windows.UI.Xaml.DispatcherTimer - Windowsランタイム
flash.utils.Timer - Adobe Flash

5. 脚注
-----

5.1. 注釈
-------

5.2. 出典
-------

6. 関連項目
-------

スレッド 
イベントループ

7. 外部リンク
--------

[1]
[2]