もうあとちょっと導入を。
前回のファサードクラスによる分離を進めてみる。
このファサードクラスは、AddCalculatorクラスを内包してしまっているため、インスタンスを外部から与えられるようにすると、柔軟性が増す。
class AddCalculatorFacade : IAdd { private: AddCalculator *pInner_; CRITICAL_SECTION cs_; public: AddCalculatorFacade(AddCalculator *pInner) throw() : pInner_(pInner) { ::InitializeCriticalSection(&cs_); } ~AddCalculatorFacade() throw() { delete pInner_; ::DeleteCrirticalSection(&cs_); } LONGLONG Add(const LONGLONG adder) throw() { ::EnterCriticalSection(&cs_); const LONGLONG result = pInner_->Add(adder); ::LeaveCriticalSection(&cs_); return result; } };
これならば、コンストラクタでインスタンスを指定することが出来る
(更にAddCalculator *を、IAdd *にすれば、もっと汎用的になる)。しつこいが、念のため例を示す。
int main(int argc, const char *argv[]) { IAdd *pAddInner = new AddCalculator(); // ここだけで選択(ファサードを挿入するかどうか) IAdd *pAdd = pAddInner; //IAdd *pAdd = new AddCalculatorFacade(pAddInner); const LONGLONG result = pAdd->Add(12345); delete pAdd; printf("Result=%I64dn", result); return 0; }
で、いよいよスレッド親和性の話になるのだが、以下の事が重要なポイントだ。
- あるコンポーネント(クラス)が、スレッドに対して安全かどうかを表明する。
- コンポーネントにアクセスしようとしているスレッドが、「安全なスレッド」かどうかを判別する。
ということが出来れば、コンポーネント毎にスレッドの安全性を「ファサード」を使って自動的に担保できるようになる。
あるコンポーネントは、最初のAddCalculatorのように、スレッドに対する安全性が無いとする。それを、単一のスレッドでしか実行しない場合は、ファサードが挿入されない。それによって、効率よくアクセスすることが出来る。
同じコンポーネントで、複数のスレッドで同時にアクセスされることがわかっている場合の、「個々のスレッド」に対して、自動的にファサードを挿入したポインタを渡す。
これで、メソッドの呼び出しが同期化されて安全にアクセスできる。
初めからスレッドセーフなコンポーネントAddCalculator2を、単一のスレッドでしか実行しない場合は、ファサードは挿入されない。
同じコンポーネントで、複数のスレッドで同時にアクセスされることがわかっている場合の、「個々のスレッド」に対してファサードは挿入されない。
なぜなら、元からAddCalculator2はスレッドセーフだとわかっているからだ。
このように、スレッドとコンポーネントの両方に、何らかの「指標」やら「属性」があれば、スレッドセーフを自動的に担保することが出来る。
そして、スレッド側に付ける「指標」は、「アパートメント」という名前の属性だ。
つづく。
最新のCOM記事:
COMについて、ローカルディスカッションで大幅に拡充してまとめ上げた記事があるので、そちらもどうぞ:「ChalkTalk CLR – COMのすべて」