SSブログ

CFRunLoopSourceの使い方その5 [考え中の問題]

昨日はスレッド間で情報のやりとりを整理するためのメカニズムのうち、unixならだいたい実装されている機能をおさらいした。今日はMacOS X独自のスレッド制御の機能について。

4  MacOS X固有のスレッド制御メカニズム

これまでのロックやメモリ障壁はMacOS X以外のOSでも存在する。MacOS X固有のメカニズムとしてrunLoopSourceがある。runLoopSourceはロックやメモリ障壁ではレベルの違う機能で、スレッド間通信とみなしたら遅くてオーバーヘッドも大きいが、複雑で大量の通信をする必要があるときには便利な機能である。前にもちょっとだけ勉強したけどもう一度整理する。

4.1  NSRunLoop

Cocoa/Objective-CのレベルではNSRunLoopはスレッドそれぞれに一つずつあって、このオブジェクトが入出力のディスパッチをしている。NSRunLoopの中でループがあって、キーボードやマウスの入力や通知(NSNotification)を監視して受信先のオブジェクトに配信したり、NSViewの再描画や設定したタイマを起動したり、ということをしている。しかしNSRunLoopのオブジェクトに直接アクセスすることはあまりないので、普段はあまり意識することはない。

Cocoa/Objective-Cでは異なるスレッド上でメソッドを実行することができる。基本的なやりかたはNSObjectのメソッド
- (void)performSelector:(SEL)aSelector
               onThread:(NSThread *)thr
             withObject:(id)arg
          waitUntilDone:(BOOL)wait;
を呼ぶ(10.5以降)。これはthrスレッド上でレシーバオブジェクトにaSelectorセレクタのメソッドを実行するものである。そのときargを引数にする。従ってセレクタはid型の引数をひとつだけとるメソッドのものでなければならない。

thrが実行中のスレッドなら単にレシーバオブジェクトにaSelectorセレクタのメソッドを投げるのと変わりないが、thrが違うスレッドだとその上でメソッドが実行される。ちなみにwaitがYESだとこのメソッドはthrスレッド上でセレクタで指定したメソッドが終了するまでブロックする。

このメソッドはthrスレッドの上でセレクタをすぐ実行するのではなく(もちろんそんなことはできない)、実はNSRunLoopの機能を使っている。このメソッドは実行先のNSRunLoopのオブジェクトにディスパッチャを組み込む。組み込まれるとNSRunLoopの次のループで実行されるという手順をとる。

unixでは作業スレッドになにかやらせたい場合、作業スレッド側でフラグをポーリングして、作業の準備ができたらメインスレッド側からフラグを書き換えて作業を開始させる、という書き方をすることになるが、Cocoa/Objective-Cではこれを使えばスレッド間で通信するまでもなく、やりたいことを直接実行できてしまうという、非常に簡単なやり方になっている。フラグの書き換えやそのロック、またそのフラグのポーリングなんてことを全く意識する必要がない非常にCocoaらしい表現になる。

4.2  CFRunLoopSource

Core Foundationのレベルではもちろんこのperform????というメソッドは使えない。従って普通のunixと同じことをしないといけないか、というともうちょっと楽な方法が提供されている。それがCFRunLoopSourceである。

Core FoundationでもCFRunLoopというのがあってこれはNSRunLoopとはToll-FreeではないけどNSRunLoopの一部分である。NSRunLoopはNSThreadなどでCocoaのレベルのスレッドを生成しないと自動的には作られないが、CFRunLoopはpthreadでスレッド生成してもCFRunLoopへのアクセスコードが実行された時点でCore Foundationが自動的に生成するようになっている。

そしてCFRunLoopにはNSRunLoopにない機能がある。それは独自の入力に対する独自のディスパッチャを追加できる、というものである。

どうするかというと、
  1. CFRunLoopSourceを初期化する
  2. CFRunLoopにrunLoopSourceを追加する
  3. 他のスレッドからrunLoopSourceを起動する
  4. runLoopSourceに指定した作業(コールバック関数)を行う
という手順になる。ようするにrunLoopSourceに作業を指定しておいて、作業開始の信号を他のスレッドからも受けられるようにしたもの、と考えていい。

ちょっと注意が必要なのはCFRunLoopSourceにはバージョンと言う情報を持っていてバージョンが0のrunLoopSourceはユーザがすべて設定するが、バージョンが1のものはMachポートが入力になる。バージョン1はMachポートを作って起動すればrunLoopに組み込まれ、そのMachポートになにか書けば他のキーボードやマウスなどと同じレベルで処理される、ということになる。

これからはバージョン0のほうだけを見る。
nice!(0)  コメント(0)  トラックバック(0) 

nice! 0

コメント 0

コメントを書く

お名前:
URL:
コメント:
画像認証:
下の画像に表示されている文字を入力してください。

トラックバック 0

献立02/08献立02/09 ブログトップ

この広告は前回の更新から一定期間経過したブログに表示されています。更新すると自動で解除されます。