[Blog翻訳] マルチスレッドによって反応し続けるアプリケーション UI にする

みなさん、こんにちは。Windows 開発統括部の古内です。

今日は 2011 年 1 月 26 日に Developing for Windows Blog に投稿された 「Keeping an application’s UI responsive with multi-threading」 の翻訳をお届けします。アプリケーションの使用中にユーザー インターフェイス (UI) が反応しなくなってハングアップ状態になる、という事態を避けるようなアプリケーション設計について説明されています。ぜひご自身のアプリケーション開発のヒントになさってください。


マルチスレッドによって反応し続けるアプリケーション UI にする

この記事では、アプリケーションの応答性について考えてみたいと思います。特に目新しい話題ではありませんが、Windows プラットフォームで動作する多くのアプリケーションを悩ませ続けている根の深い問題です。私も開発者としてこの問題に直面しましたが、基本的なしくみをある程度を理解したところで、実はその原因のほとんどがアプリケーションの設計にあるということに気付きました。一方、エンドユーザーの見解はどうかと言えば、本当の原因はアプリケーションのマズい設計にあるにもかかわらず、自分が使用しているシステムにあると考え、そのイライラをシステムにぶつけているようです。

アプリケーションの反応の低下にはさまざまなタイプがあります。ユーザー インターフェイスの更新が不安定になる/遅延する、ユーザーの入力がまったく受け付けられない、アプリケーションのインターフェイスがアクセス不能になりタスク マネージャーに 「応答なし」 と表示されるなど、実にさまざまです。こうした問題を防ぐことは、概念的にはかなり簡単です。UI ブロック処理をワーカー スレッドに分離すればよいのです。Windows アプリケーションでは、ユーザー インターフェイスのスレッドがそのプロセスのプライマリ スレッドです。このスレッドが WinMain を実行し、アプリケーションのメイン ウィンドウのメッセージ ポンプを処理します。このスレッドで何らかの長々しい処理が実行されると、メッセージの受け渡しが行われず、メイン ウィンドウが応答しなくなります。「長々しい」というのはやや主観的な意見ですが、短い処理でももちろん、応答性に支障が出ることはあります。特に、短い処理を連続的に実行するとその影響が累積し、問題が発生します。

私は、ワーカー スレッドに適した処理のタイプを、「アクティブ」 と 「パッシブ」 の 2 つに分けて考えています。パッシブな処理というのは、ユーザーにほとんど意識されないような処理のことです。場合によっては、ユーザーはそれらの処理が実行されたことすら気付きません。たとえば、ファイル ブラウザーがファイルのプロパティを表示したり、サムネイル画像を作成したりする処理がそうです。一方、アクティブな処理とは、ユーザーによって明示的に呼び出される処理のことです。たとえば、ユーザーがボタンを押し、時間をかけて複雑なスプライン曲線を描くような場合の処理です。どちらのタイプにしても、ユーザー インターフェイスのデザインとプログラム コードの設計について、ある程度考慮する必要があります。例として、ファイル ブラウザーにファイルの一覧が表示される場合を考えてみましょう。ユーザーがファイルをいくつか選択すると、そのファイルの詳細情報が次々と表示されていきます (パッシブな処理)。この選択操作がすばやく行われると、先に選択したファイルの詳細情報を抽出中であるにもかかわらず、その抽出結果は不要になってしまいます。このため、アプリケーションはこうした操作にも対処できるよう設計する必要があります。たとえば、ワーカー スレッドをミサイルの追尾機能のように一方的に処理にしたり、キャンセルできるようにすることで解決できます。一方、アクティブな処理の場合には、明示的にキャンセルできる設計にし、インターフェイスで処理の進捗状況をユーザーに知らせるようにするのが一般的です。

では、開発者はどのような場合に、ワーカー スレッドに処理を任せればよいのでしょうか。先に取り上げたような明らかな場合もありますが、判断しづらいケースもあります。たとえば、Windows API の呼び出しでは一見問題がないのに、実はスタックのどこかで何らかの I/O を呼び出しているような場合です。このようなケースを判断するために役に立つツールがあります。Visual Studio に組み込まれているプロファイラーも便利なツールですが、マイクロソフトは、パフォーマンスが極めて根本的な問題であることから、パフォーマンス分析用の優れたツールセットを Windows Performance Analysis Developer Center (英語) で提供しています。さらに、Windows 用デバッグ ツールや Microsoft シンボル サーバーを使用して、Windows シンボルをダウンロードすることもできます。多くの場合、Windows API の呼び出しに伴うコール スタックの階層全体を調べることができれば、マルチスレッドに適した処理かどうか判断できます。

応答性に優れたアプリケーションを設計しやすいように、Windows API には、マルチスレッド API やマルチスレッド サービスに対応した機能が豊富に用意されています。先ほどパッシブな処理としてご紹介したシナリオは、Windows スレッド プール サービス (英語) を使用するのが最適な場合があります。Windows スレッド プールを使用すると、アプリケーションで必要なスレッド管理を減らし、作業項目の実行をシステムでスケジューリングして、最適化することができます。スレッド プールがニーズに合わない場合や、  スレッド プールと互換性のない要件がある場合 (STA COM オブジェクトを使用する必要がある場合など) には、マルチスレッド対応のベース API、同期プリミティブ、待機関数 (英語) を使うことができます。

ワーカー スレッドを正しく利用して正しく動作するアプリケーションを設計するのは簡単なことではありません。 しかし、 まったく手に負えない作業という訳でもありません。 その努力はきっとユーザーに高く評価されるはずです。もっとも、どれほどの苦労があったかまでは、ユーザーに伝わらないのかもしれませんが。。。