その2では、ネイティブリソースをミュータブルなクラスでラップする方法を示します。イミュータブルなクラスでラップ したほうが効率的だと思いますが、こちらの方法が必要な場合もあるかもしれません。ナイーブな実装は↓のようになります。
class A { protected IntPtr handle; public A() { handle = Api.Alloc(); } ~A() { Release(); } public virutal Release() { if (handle == IntPtr.Zero) release; Api.Release(handle); handle = IntPtr.Zero; } public void Method() { Api.Method(handle); } }
Api
クラスでネイティブ API の呼びだしが実装されていることを想定した疑似コードです。実際には、Api.Alloc
では、ILCreateFromPathW
でアイテムIDリストを作成したり、Api.Release
では、ILFree
で開放したりします。
このコードでは、handle がスレッド間で共有されることが考慮されていないので、ネイティブ API によっては、クラッシュします。また、c# では、ファイナライザーの実行タイミング
がかなりアグレッシブなため、稀に不正なメモリーアクセスなどの原因で、環境によっては、クラッシュします。
Api.Method(handle)
実行中に、~A()
が呼ばれる場合があるためです。これを避けるには、↓のようにします。
class A { protected IntPtr handle; protected Object lockObj = new Object(); public A() { handle = Api.Alloc(); } ~A() { Release(); } public virutal Release() { lock (lockObj) { if (handle == IntPtr.Zero) release; Api.Release(handle); handle = IntPtr.Zero; } } public void Method() { lock (lockObj) { Api.Method(handle); } } }
handle
を使う場合には、lock
により、複数スレッドから同時にアクセスすることないよう保護します。当然、
handle
使用中にデストラクターが実行されることはありません。もちろん、
handle
は
lock
外へ公開してはいけません。やむを得ず公開する場合には、
handle
使用中は、同じように
lockObject
で保護する必要があります。
メソッド呼び出し毎に、同期オブジェクトが作成されるため、あまり高速な動作は期待できません。メソッドが頻繁に呼び出される場合には、イミュータブルクラスでラップする方法 をお勧めします。
このサイトのページへのリンクは自由に行っていただいてかまいません。
このサイトで公開している全ての画像、プログラム、文書の無断転載を禁止します。
ここをクリック
すると表示されるページから作者へメールで連絡できます。