2009-02-20 13 views
10

作成するのに高価なオブジェクトがあります。これは、管理されていないリソースを使用します。このリソースは、完了したら明示的に解放しなければならないため、IDisposable()を実装します。私は、これらの高価なリソースのためのキャッシュを作成して、作成コストを最小限に抑えたいと思いますが、処分の対処方法を知りません。キャッシュされたIDisposableオブジェクトを管理する方法は?

オブジェクトを使用するメソッドが処理の責任を負う場合は、キャッシュ内に配置されたインスタンスが作成され、再作成する必要があり、キャッシュのポイントを破棄します。私がそれらを使用するメソッドにオブジェクトを配置しないと、決して処理されません。私はキャッシュから取り除かれたときにそれらを処分できると思っていましたが、その後メソッドで使用されているインスタンスを処分することになりました。

有効範囲外に出てガベージコレクタによって収集され、その時点でリソースが解放されるのは有効ですか?これは間違っていて使い捨てであるという考えに逆らっています...

答えて

4

使い捨てオブジェクトには、常に処分を担当する明確な所有者が必要です。しかし、これは常にそれらを作成したオブジェクトではありません。さらに、所有権を移転することもできます。

これを実現するには、解決策が明らかになります。 処分、リサイクルしないでください!キャッシュからリソースを取得する方法だけでなく、それを返す方法も必要です。その時点で、キャッシュは再び所有者であり、将来の使用のためにリソースを保持するか、または廃棄するかを選択できます。

public interface IDisposableItemCache<T> : IDisposable 
     where T:IDisposable 
    { 
     /// <summary> 
     /// Creates a new item, or fetches an available item from the cache. 
     /// </summary> 
     /// <remarks> 
     /// Ownership of the item is transfered from the cache to the client. 
     /// The client is responsible for either disposing it at some point, 
     /// or transferring ownership back to the cache with 
     /// <see cref="Recycle"/>. 
     /// </remarks> 
     T AcquireItem(); 

     /// <summary> 
     /// Transfers ownership of the item back to the cache. 
     /// </summary> 
     void Recycle(T item); 

    } 

編集:私はこの考えはまた、それがobject poolと呼ばれる春、中に存在していることに気づきました。それらのBorrowObjectReturnObjectのメソッドは私の例のメソッドと一致します。

+0

私は基本的にこれとNoBugzの答えのハイブリッドであった解決策に終わったのです。ありがとう –

2

管理されていないリソースを管理対象インスタンスから切り離し、キャッシュマネージャを使用して管理されていないリソースを保持できます。管理対象オブジェクトは、キャッシュマネージャからアンマネージリソースのインスタンスを取得しようとします。キャッシュマネージャは、そのキャッシュマネージャからインスタンスを作成するか、キャッシュから1つの空きインスタンスを取得して、廃棄時にそのインスタンスを廃棄する代わりにキャッシュマネージャに返します。キャッシュマネージャは、アンマネージドリソースを必要に応じて割り当ておよび解放するための唯一の責任オブジェクトです。

3

まず、ネイティブリソースをラップするタイプは、使い捨てだけでなく、ファイナライズ可能である必要があります。さらに、SafeHandleを使用してネイティブリソースをラップします。

誰かが明示的に責任を負い、アイテムを処理して処分することができる場合を除き、GCが処理することをお勧めします。しかし、それはファイナライズ可能でなければならないことに注意してください。さもなければ、GCはそれをもう一度見せません。

4

(誤)引用レイモンド・チェンへ:すべてのキャッシュの有効期限ポリシーのないだから漏れ

で、明確なキャッシュの有効期限ポリシーを設定し、キャッシュが通常のケースとして、それらを処分しましょう。これにより、アプリケーションのシャットダウンが処理されたままになります。

管理対象外のリソースがプロセスによって所有されている場合は、シャットダウン時にそれらのプロセスを解放することができます。

プロセスによって所有されているではない場合は、シャットダウンを検出してキャッシュされた要素を明示的に廃棄する必要があります。

プロセスのシャットダウンを確実に検出できず、管理対象リソースが高価な場合は、アンマネージドリソースが高価なため、アンマネージドリソースから管理対象を分離し、管理対象リソースのみを保持させます。

非管理対象リソースが高価でキャッシュを必要とし、プロセスが所有しておらず、プロセスシャットダウンを確実に検出できず、それらをリークできない場合、問題を解決できません。

0

オブジェクトは、オブジェクトを作成するクラスで処理する必要があります。あなたの呼び出し元はキャッシュ内にアイテムを作成していないので、それらのアイテムを処分することもありません。

私は、「クラスの取得」のではなく、発信者が作成するための責任ではないことを強調するために、「クラスを作成します」あなたのファクトリメソッドは、のようなものを命名されたことを確認するため、いないためだろう廃棄。

+0

"クリエイターは所有者"とは考えられるデザインガイドラインですが、回答が示すとおり、クリエイター(工場または工場クライアント)が誰であるかは必ずしも明確ではありません。これを明確かつ明示的に文書化することが望ましいです。自分の答えが示すように、所有権を譲渡することさえできます。 –

1

これは、クラスファクトリとIDisposableで解決できます。例:

public class CachedObject : IDisposable { 
    private int mRefCount; 
    private CachedObject(int something) { 
    mRefCount = 1; 
    } 
    public static CachedObject CreateObject(int something) { 
    CachedObject obj = LookupInCache(something); 
    if (obj != null) Interlocked.Increment(ref obj.mRefCount); 
    else obj = new CachedObject(something); 
    return obj; 
    } 
    private static CachedObject LookupInCache(int something) { 
    CachedObject obj = null; 
    // Implement cache lookup here, don't forget to lock 
    //.. 
    return obj; 
    } 
    public void Dispose() { 
    int cnt = Interlocked.Decrement(ref mRefCount); 
    if (cnt == 0) { 
     // Remove from cache 
     // 
    } 
    } 
} 
+0

このソリューションには、所有権の概念が歪められているため、予期しない影響があります。クライアントコードはdisposeを呼び出す責任がありますが、オブジェクトが変更可能な場合は、同じオブジェクトで他のオーナーが作業している可能性があることも認識する必要があります。 –

+1

いいえ、変更可能な場合はキャッシュしないでください。 –

+1

このクラスはスレッドセーフではありません。インターロックされたinc/decは、ifガードを保護しません。 –

関連する問題