2012-01-03 15 views
2

はIDisposableをパターン+ファイナライザパターンを見ると、私は理解していないものがあります。IDisposableを+ファイナライザパターン

1)Dispose()を呼び出すとGC.SuppressFinalize(this)が呼び出されます。これは、オブジェクトがファイナライザキューに既に適切に配置されていてはならないことを意味しますか?それはオブジェクトをより速く解放するのに役立ちますか?

2)しかし、私はこのオブジェクトに対してDispose()を呼び出さない場合はどうしますか?その場合、ファイナライザは正しく動くはずですか?しかし、廃棄する(偽)。絶対に何もしません(唯一の設定は= true)。これは意図されていますか?それは何かが欠けているかのように感じます...

答えて

0

質問1:はい、GC.SuppressFinalizeが呼び出されない場合、オブジェクトはファイナライザキューに置かれ、世代を上げます(まだgen 2ではない場合)。つまり、そのオブジェクトのメモリは、新しい世代のGCの次のパスまで再利用されません。

質問2:あなたのコメント//shared cleanup logicは、共有クリーンアップロジックが行くところです。これは、disposed = true以外の設定が行われる場所です。また

、脇:あなたの処分ロジックは一度だけ呼び出されるべき場合には、 lock取得を検討し、無競争のロックは.NETで非常に高速です:

public class ComplexCleanupBase : IDisposable 
{ 
    private volatile bool disposed = false; // to detect redundant calls 
    private readonly object _mutex = new object(); 

    protected virtual void Dispose(bool disposing) 
    { 
    if (!Monitor.TryEnter(_mutex)) 
    { 
     // We're already being disposed. 
     return; 
    } 

    try 
    { 
     if (!disposed) 
     { 
     if (disposing) 
     { 
      // dispose-only, i.e. non-finalizable logic 
     } 

     // shared cleanup logic 
     disposed = true; 
     } 
    } 
    finally 
    { 
     Monitor.Exit(_mutex); 
    } 
    } 
    ... other methods unchanged 
} 
+0

ロックが非常に長く保持されない場合でも、ファイナライザはロック取得をブロックするべきではありません。何かがロックを不適切に保持すると、他のファイナライザが実行されなくなる可能性があります。 – supercat

+0

@supercatこの例では、コールがファイナライザ内でブロックすることは不可能です。 '_mutex'は、処分目的でのみ使用するべきです。一般的にあなたは正しいですが、この場合、あなたのポイントはすべきではありません。 –

+0

@supercat私の前のコメントが間違っている状況を考えました:別のインスタンスのファイナライズ中に、それがアプリケーションに別のルートを持つように、 'ComplexCleanupBase'のインスタンスを元気に戻す場合、mutexで競合する可能性がありますしかし、これは本当に*できないはずです。回答はすべて更新されました。 –

0

(偽)廃棄が予定されていない場合あなたのクラスもそれから派生したクラスもC#スタイルの "デストラクタ"を含んでいてもFinalizeを上書きしてはならないという非常に良い兆候であり、 "disposing"引数はダンプとみなされます。 protectedパブリックメソッドと異なるシグネチャを破棄します。

親クラスがこのような動作を期待していないときにデストラクタを実装するか、派生クラスでFinalizeをオーバーライドすると、Heisenbugが生成されることに注意してください。とりわけ、GCは、クラスのフィールドで参照されるエンティティが使用されている間でも、クラスオブジェクトが放棄され、ファイナライザ/デストラクタをトリガすることを決定することがあります。たとえば、静的クラスを想定usbThingieは、整数のハンドルを使用してUSBコントローラを操作して、ラッパークラスusbWrapperのような何かを行います。それは前のsendData()呼び出しはusbWrapperのインスタンスに行わ最後のものである場合

 
    UInt32 myHandle; 
    void sendData(Byte data[]) 
    { 
    UsbThingie.send(myHandle, data[0], data.Length); 
    } 

を放棄された場合、一度UsbThingie.send()が呼び出されると、たとえそれが返される前であっても、usbWrapperへの参照は存在しないので、ガーベジコレクタはファイナライザを安全にトリガすることができます。ファイナライザがmyHandleで示されたチャネルを閉じることを試みると、発生していた伝送が中断する可能性があります。 usbThingieがスレッドセーフでない場合、何が起きるかは分かりません。