OS-Thread sensitive 対応

Win32 のライブラリには、OS のスレッドに対して敏感な API セットを提供するものがある。これにはウィンドウハンドルやデバイスコンテキストといったものを直接的・間接的に扱う場合や、スレッドローカルストーレジに依存したライブラリに良く見られる。
WinForms では上記のような場合には InvokeRequired プロパティを通じてスレッドの調停を要求するようになっています。*1 この手法では、コンシューマによってオブジェクトに対する操作を適切にマーシャリングすることを要求する方法であって、コンシューマにたいして正確な操作と知識を要求してしまいます。
id:akiramei:20060125#p2 で少し話がありますが、現状の Microsoft .NET Framework という CLR の実装では、Assign to OSThread は Thread の生成時に1回だけ発生するため、特定のソフトスレッドは常に同じハードスレッドの上で動作するようになっていると思われます。*2 この特性を利用し、CLR の仕組みを通じて自動的にスレッドスイッチを行うようなクラスを生成することができます。

  • 状態を保持する型を ContextBoundObject の派生クラスとする
  • 上記の型に Context としてスレッドスイッチを実施する実装を付与する
  • スイッチ先となるソフトウェアスレッドを固定する
  • 上記のスレッドの状態をかなり厳密に管理し、ブロック状態を極力減らす

ということが必要になります。
特に最後の点は重要で、id:akiramei:20041009#p1 のような GC からの呼び出しにおいて、コンテキスト切り替えで別スレッドを長時間待機なんてやったりしたら、CLR 全体のパフォーマンスや、newobj などの挙動に大きく影響が発生する可能性があります。*3

具体的な実装は……仕事場で書き始めるとアレな量になるので、今回は無しです。(サーバシンクだけでやれるかな?)

*1:有名ですが、.NET 1.1 以前と .NET 2.0 では InvokeRequired プロパティを無視した場合の挙動が変化しているのにも注意しなければなりません。

*2:逆に、1つのハードスレッドは複数のソフトスレッドを割り当てられる場合があります。特に1つの Win32 Process に対して複数のアプリケーションドメインをロードした場合などに顕著になるという話です。

*3:API セットへアクセスするためのスレッドに対してジョブを詰んで ReRegisterForFinalize() を呼ぶのがよさそう?