2011-01-17 3 views
3

これは私が確かめるためだけに、私はこの権利を持っています:IDisposable with destructor:スレッドセーフ実装が必要ですか?

私たちはIDisposalパターンを実装する大きなリソースクラスを持っています。それは、(たとえ正確に1回だけ呼び出しても)1回以上呼び出されるように(意図的に)実装されるべきです。また、Dispose()メソッドを呼び出すFinalizerも実装しています。手動で呼び出すと、Dispose()はGC.SuppressFinalize(this)も呼び出します。

処分パターンのいくつかの例があります。彼らのほとんどは、処理コードのエンドでGC.SuppressFinalize(this)と呼んでいます。 Dispose()メソッドの初めにの前に呼び出すことをお勧めします。の前にクリーニングしてください。後者は、GCがファイナライザを同時に呼び出していないことを確認します。

質問:
GC.SuppressFinalizeを最初に置いているようですが、これ以上はありませんか?我々はまだ競争状態を持っていますよね?だから、代わりにスレッドセーフな方法でDispose()を実装する必要があります。

+0

.NET Frameworkのフレームワーク設計ガイドラインで説明されているように、disposeパターンは最後に 'GC.SuppressFinalize'コールを持っています。このガイドラインはマイクロソフト社内で大きく議論されており、最終的にパターンはこのように終わった。 – Steven

答えて

2

一見したライブ参照が存在する間にオブジェクトをファイナライズすることは時々ありますが、他のものがオブジェクトを参照することがない場合にのみ起こります。 GC.SuppressFinalize(this)は現在のオブジェクト 'this'を積極的に参照し、GC.SuppressFinalizeが実行されるまでファイナライズされないことを保証します。さらに、オブジェクト参照がオブジェクトを処理するために存在し、Disposeメソッドで使用可能であったという事実は、オブジェクトが死んでいない限りファイナライザがキューに入れられなかったことを保証します。 、または他のオブジェクトのもの)がそれを復活させました。

オブジェクトの確定がスケジュールされ、それを認識することなく復活するシナリオがいくつか存在するため、処理を守り、冗長な操作からファイナライズすることは悪い考えではありません。しかし、Microsoftのパターンは良いものではありません。ファイナライズ可能オブジェクトは、ファイナライズに必要のないオブジェクトへの参照を保持すべきではありません。オブジェクトに管理対象リソースと非管理対象リソースが混在している場合、管理対象外のリソースをそれぞれのクラスに移動して管理対象リソースにする必要があります。

4

GCは、到達可能でないオブジェクトのみをクリーンアップします。

thisポインタがスタック上にあるため、コードが実行されているクラスはまだ「到達可能」です。したがって、disposeが実行されている間、ファイナライザは呼び出されません。

SuppressFinalizeを開始時または終了時に呼び出すかどうかは関係ありません。以下のコメント主が示されているように

は、CLRの実装では、オブジェクトがガベージコレクト/確定インスタンスメソッドの実行中に取得していません保証には表示されません。オブジェクトを生きたままにする唯一可能な「信頼できる」参照は、オブジェクトのメソッドを呼び出すために使用されるものですが、JIT内部についてはそれについてのステートメントを作成するのに十分ではなく、動作が変更される可能性があります。

ここでは、以下の説明にアクセスするための回答を残しています。

+1

これはあまり正確ではありません。実行中のインスタンスメソッドはファイナライズを妨げません。ジッタは、ローカル変数またはメソッド引数の場合と同じように、GCに対する* this *の有効期間情報を提供します。しかし、はい、オブジェクトにまだ触れるコードは実行されないという保証があります。 –

+0

@ jdv-Jan de Vaan:これはほとんどの場合当てはまります。ただし、少なくとも1つの例外が存在します:http://blogs.msdn.com/b/ricom/archive/2004/05/19/135332.aspx(そのサイトのさらに下のディスカッションに続きます) - 少なくともそれを念頭に置き、それらの(まれな)状況に備えるべきです。 – user492238

+0

@Hans Passant:インスタンスメソッドが呼び出される前に、このポインタがスタックにプッシュされていると仮定するのは間違っていると言っていますか?または、CLRがコードの実行を開始する前に積極的にスタックをクリアしていますか?最後に参照された後でローカル変数がGCによって考慮されないことはわかっていますが、この場合ポインタは(暗黙の)関数の引数です。 –

1

Disposeは例外をスローするべきではありません。

Disposeのすべてのコードがスレッドセーフであることを確認して、呼び出されたときに何か変なことがないようにします。変数がnullであるかどうかをチェックするのが普通です。

私は、Dispose関数の最後にGC.SuppressFinalizeを見ただけです。

関連する問題