その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
で保護する必要があります。
メソッド呼び出し毎に、同期オブジェクトが作成されるため、あまり高速な動作は期待できません。メソッドが頻繁に呼び出される場合には、イミュータブルクラスでラップする方法 をお勧めします。
このサイトのページへのリンクは自由に行っていただいてかまいません。
このサイトで公開している全ての画像、プログラム、文書の無断転載を禁止します。
ここをクリック
すると表示されるページから作者へメールで連絡できます。