nsIThreadPoolの使い方
Firefox3からThread関係のインターフェースが追加されています。
ネットワークの通信処理をかいているのですが、nsIAsyncInputStreamやnsIAsyncOPenStreamの、AsyncWait()の使い方がわからないので、ベーシックな感じでThreadで処理しています。
# AsyncWait()を使う場合, nsIBinaryInputStreamって使っていいのかな。
基本的なThreadの処理はMDCにあるサンプルコードを見ればわかります。
しかし、処理ごとにThreadを作っていると、Thread管理するためのコードが必要になったり、Thread数が爆発してしまったりします。
さて、XULPlanetのXPCOMの項目にThread関係があります。
nsIThread
http://www.xulplanet.com/references/xpcomref/group_XPCOM.html
nsIThreadEventFilter
nsIThreadInternal
nsIThreadJSContextStack
nsIThreadManager
nsIThreadObserver
nsIThreadPool
ここを見ると、nsIThreadPoolというインターフェースがあるのがわかると思います。
ThreadPoolでは、あらかじめいくつかのThreadを用意しておき、処理が割り当てられると用意しているThreadをその処理に割り当てます。
また、一定以上の処理の割り当てによって、用意しているThreadが足りなくなると、処理中のThreadがあくまで処理を待機します。
この機能によって、Threadを使った処理のキューイングのようなことができ、また、簡単に並列に実行する処理数を変更することもできます。
nsIServerSocketを使うときとか、かなり便利です。(てか、それを使うために調べたんですが)
では、使い方をサンプルコードにて。
// XPCOMUtilsオブジェクトをロード // XPCOMUtils.generateQI を利用すると、 QueryInterfaceを簡単に記述可能 Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); // nsIThreadManagerサービスを取得 var ThreadManager = Components.classes["@mozilla.org/thread-manager;1"].getService(); // thread pool を動作させるためのスレッドを用意 //var thread = ThreadManager.newThread(0); // thread pool を作成 var threadPool = Components.classes["@mozilla.org/thread-pool;1"].createInstance(Components.interfaces.nsIThreadPool); // thread で処理したいお仕事 var work = function(id) { this.workId = id; } work.prototype = { run: function() { dump("お仕事" + this.workId + "\n"); }, QueryInterface: XPCOMUtils.generateQI([Components.interfaces.nsIRunnable]) } // thread に thread pool を割り当て //thread.dispatch(threadPool, thread.DISPATCH_NORMAL); // お仕事を thread pool に割り当て threadPool.dispatch(new work(1), threadPool.DISPATCH_NORMAL); threadPool.dispatch(new work(2), threadPool.DISPATCH_NORMAL); threadPool.dispatch(new work(3), threadPool.DISPATCH_NORMAL); threadPool.dispatch(new work(4), threadPool.DISPATCH_NORMAL);
といった感じ。
nsIThreadPoolは nsIRunnable を実装しているので、thread.dispatch() に渡してやることで、threadPool の処理をバックグラウンドで実行を開始します。
訂正: nsIThreadPool用にThreadを用意しなくてもdispatchで処理を割り当てると呼び出しThreadをブロックすることなく実行してくれるみたいです。
あとは、threadPool.dispatch() に、通常のThread処理と同じように nsIRunnableインターフェースを実装したオブジェクトを渡してあげると、threadPoolが割り当てられた処理をさらに別スレッドで動かしてくれます。
thread poolのthread数の調整ですが、
Properties
PRUint32 idleThreadLimit
http://www.xulplanet.com/references/xpcomref/ifaces/nsIThreadPool.html
Get/set the maximum number of idle threads kept alive.
PRUint32 idleThreadTimeout
Get/set the amount of time in milliseconds before an idle thread is destroyed.
PRUint32 threadLimit
Get/set the maximum number of threads allowed at one time in this pool.
と、プロパティがあります。 threadLimit が処理を行う最大スレッド数です。これを増減すれば同時に処理するスレッド数が変わると思います。
が、未確認です。
なぜ未確認か。それは、ThreadPoolを利用する処理を組み込み、ThreadPoolのすべての処理が終了すると、Firefoxがおちるという症状が発生しています。
状況整理もかねて、このエントリを書いています。
原因が上記のコードに含まれている場合はあとで修正します。
追記:
とりあえず、単純な処理でThreadPoolを使う上ではFirefoxがクラッシュするということはありませんでした。
今、書いているコードでクラッシュするのは通信処理との兼ね合いのよう。
nsISocketTransportは、nsITransportを継承していて、nsITransportは nsIStreamTransportService から生成されます。そして、nsIStreamTransportService内部ではnsIThreadPoolを使っています。
nsISocketTransportがどう生成されるのか次第ですが、nsIStreamTransportServiceと競合してるような・・・そんな気が。
もっと詳しく調べてみます・・・.