The Thread Manager

by 3 contributors:

Firefox 3 で導入されたスレッドマネージャは、スレッドを作成して、処理を行うイベントをそのスレッドに割り当てる便利な方法を提供します。

インタフェース

スレッドのサポートを提供するインタフェースは、以下のようにいくつか存在します。

nsIThreadManager
スレッドを作成できるようにするスレッドマネージャそのもの。
nsIThread
この nsIThread インタフェースは、オペレーティングシステムのスレッドをカプセル化したもので、コードからマルチスレッドに対する簡易なクロスプラットフォームアクセスを提供します。
nsIThreadPool
スレッドプールは、限られた一連のワーカースレッドを提供します。イベントをプールに割り当てる際、プールは、そのイベントを処理するために利用可能なワーカースレッドを選択する役割を果たします。
nsIThreadInternal
nsIThread のサブクラスで、XPCOM のスレッドオブジェクトによって実装されており、スレッドへのアクティビティ割り当て監視サポートを提供します。
nsIThreadObserver
スレッドを監視する機能を提供します。スレッドにイベントが割り当てられた際や、それらのイベントの処理が完了した際に、通知を受け取ることができます。
nsIThreadEventFilter
このインタフェースは nsIThreadInternal 内の pushEventQueue() メソッドで使われており、イベントのフィルタリングを可能にします。

スレッドマネージャの使い方

スレッドマネージャを使用するには、各スレッドのワーキングコードを nsIRunnable XPCOM オブジェクトにカプセル化しなければなりません。このオブジェクトは全体を JavaScript で書くことができ、それほど難しくありません。

註: DOM はスレッドセーフではないので、バックグラウンドスレッドから DOM や ユーザーインタフェースにアクセスしてはいけません。クラッシュの原因となります。

このセクションでは簡単な例を見ていきます。

バックグラウンドスレッド

まず、バックグラウンドスレッドで行われる処理を扱う XPCOM オブジェクトが必要になります。

var workingThread = function(threadID, number) {
  this.threadID = threadID;
  this.number = number;
  this.result = 0;
};

workingThread.prototype = {
  run: function() {
    try {
      // ここでワーキングスレッドが処理を行う
      
      for (var i = 0; i<= this.number; i++) {
        this.result += i;
      }
      
      // 処理が終了したら、終了を知らせるためにメインスレッドにコールバックする
      
      main.dispatch(new mainThread(this.threadID, this.result),
        background.DISPATCH_NORMAL);
    } catch(err) {
      Components.utils.reportError(err);
    }
  },
  
  QueryInterface: function(iid) {
    if (iid.equals(Components.interfaces.nsIRunnable) ||
        iid.equals(Components.interfaces.nsISupports)) {
            return this;
    }
    throw Components.results.NS_ERROR_NO_INTERFACE;
  }
};

このスレッドのコンストラクタでは、スレッドの ID と 数値をローカル変数に保存し、result 変数を 0 に設定しています。 これらの変数はスレッドの実行時に使用されます。

このオブジェクトにはコンストラクタのほかに 2 つのメソッドがあります:

run()
run() メソッドは nsIThread インタフェースの dispatch() メソッドが呼び出されたときに呼び出されます。これはバックグラウンドスレッドで実際の作業を行うルーチンです。 この例では、0 から <tt>this.number</tt> までのすべての数の合計を計算しています。 計算が終了すると、 mainThread オブジェクトを使ってメインスレッドにアクセスし、計算結果を共有するためにコールバックをメインスレッドにディスパッチします。
QueryInterface()
スレッドの XPCOM オブジェクトは nsIRunnable インタフェースを扱う必要があるため、オブジェクトが nsIRunnable インタフェースを扱っているかを尋ねるためにこのメソッドが呼び出されたときに、正しい反応を返さなければなりません。

メインスレッド

メインスレッドを扱う XPCOM オブジェクトはバックグラウンドタスクからのコールバックとして使用されます。このオブジェクトの run() メソッドは、バックグラウンドスレッドが計算結果をユーザーに知らせようとしたときに呼び出されます。バックグラウンドスレッドはユーザーインタフェースに触れることができず、メインスレッドに依頼しなければならないため、このオブジェクトが必要になります。

var mainThread = function(threadID, result) {
  this.threadID = threadID;
  this.result = result;
};

mainThread.prototype = {
  run: function() {
    try {
      // ここでワーキングスレッドの完了に対して反応を返す
      alert('Thread ' + this.threadID + ' finished with result: ' + this.result);
    } catch(err) {
      Components.utils.reportError(err);
    }
  },
  
  QueryInterface: function(iid) {
    if (iid.equals(Components.interfaces.nsIRunnable) ||
        iid.equals(Components.interfaces.nsISupports)) {
            return this;
    }
    throw Components.results.NS_ERROR_NO_INTERFACE;
  }
};

この例では run() メソッドは単純に警告ボックスを使ってユーザーに出力を表示します。

仕上げ

実際にスレッドマネージャを使ってバックグラウンドでこれらの計算を行うには、まず <tt>workingThread</tt> のタスクを実行する nsIThread オブジェクトを作成する必要があります:

var background = Components.classes["@mozilla.org/thread-manager;1"].getService().newThread(0);

それに加えて、メインスレッドの nsIThread の参照を得る必要があります:

var main = Components.classes["@mozilla.org/thread-manager;1"].getService().mainThread;

この情報を得たら、タスクをバックグラウンドスレッドに割り当てることができます。

background.dispatch(new workingThread(1, 5000000), background.DISPATCH_NORMAL);

これによりバックグラウンドスレッドの実行が開始され、0 と 5,000,000 の間のすべての数の合計が計算されます。作業が終了すると、メインスレッドの run() メソッドが呼び出され、結果をユーザーと共有します。それまでの間、メインスレッドはユーザーの操作に反応するなどの自分の作業を続けることができます。


ドキュメントのタグと貢献者

タグ: 
Contributors to this page: Mgjbot, Shoot, Kohei
最終更新者: Mgjbot,