web-dev-qa-db-ja.com

C#:スマートキャッシュを実装する方法

ある種のキャッシュを実装すると便利な場所がいくつかあります。たとえば、カスタム文字列に基づいてリソースルックアップを実行する場合、リフレクションを使用してプロパティの名前を検索する場合、またはプロパティ名ごとに1つのPropertyChangedEventArgsのみを持つ場合です。

最後のものの簡単な例:

public static class Cache
{
    private static Dictionary<string, PropertyChangedEventArgs> cache;
    static Cache()
    {
        cache = new Dictionary<string, PropertyChangedEventArgs>();
    }
    public static PropertyChangedEventArgs GetPropertyChangedEventArgs(
        string propertyName)
    {
        if (cache.ContainsKey(propertyName))
            return cache[propertyName];

        return cache[propertyName] = new PropertyChangedEventArgs(propertyName);
    }
}

しかし、これはうまくいくでしょうか?たとえば、さまざまなpropertyNameが大量にロードされている場合、ガベージコレクションなどが行われることなく巨大なキャッシュが存在することになります。キャッシュされているものがより大きな値であり、アプリケーションが長時間実行されている場合、これは一種の問題になる可能性があると想像しています...またはあなたはどう思いますか?適切なキャッシュをどのように実装する必要がありますか?これはほとんどの目的に十分ですか?理解するのが難しくない、または実装するのが複雑すぎる、いくつかのニースキャッシュ実装の例はありますか?

19
Svish

キャッシュされた各アイテムを WeakReference でラップできます。これにより、GCは必要に応じてアイテムを再利用できますが、アイテムがキャッシュから消えるタイミングをきめ細かく制御したり、明示的な有効期限ポリシーなどを実装したりすることはできません。

(Ha! MSDNページ に示されている例が単純なキャッシュクラスであることに気づきました。)

18
LukeH

これは大きな問題です。問題のドメインを特定し、正しい手法を適用する必要があります。たとえば、オブジェクトの有効期限をどのように説明しますか?それらは一定の時間間隔で古くなりますか?それらは外部のイベントによって古くなりますか?これはどのくらいの頻度で発生しますか?さらに、オブジェクトはいくつありますか?最後に、オブジェクトを生成するのにどれくらいの費用がかかりますか?

最も簡単な戦略は、上記のように、まっすぐなメモ化を行うことです。これは、オブジェクトが期限切れになることはなく、メモリを枯渇させるほど多くはないことを前提としていますandこれらのオブジェクトを作成するためのコストは、最初にキャッシュを使用する必要があると考えます。

次の層は、オブジェクトの数を制限し、LRU(最近使用されていない)などの暗黙的な有効期限ポリシーを使用することです。これを行うには、通常、辞書に加えて二重にリンクされたリストを使用し、オブジェクトにアクセスするたびに、リストの先頭に移動します。次に、新しいオブジェクトを追加する必要があるが、オブジェクトの総数の制限を超えている場合は、リストの最後から削除します。

次に、時間または外部刺激に基づいて、明示的な有効期限を強制する必要がある場合があります。これには、呼び出すことができるある種の有効期限イベントが必要になります。

ご覧のとおり、キャッシングには多くの設計があるため、ドメインを理解し、適切に設計する必要があります。あなたは私が詳細を議論するのに十分な詳細を提供しなかったと私は感じました。

P.S.クラスを定義するときは、ジェネリックスの使用を検討してください。これにより、多くの種類のオブジェクトを格納できるため、キャッシュコードを再利用できます。

25
Adam Luter

.NET 4.0は、さまざまな種類のものをキャッシュするためのSystem.Runtime.Cachingをサポートするようになりました。車輪の再発明ではなく、最初にそれを調べる必要があります。詳細:

http://msdn.Microsoft.com/en-us/library/system.runtime.caching%28VS.100%29.aspx

9
Brendan Byrd

これは素晴らしい議論ですが、アプリケーションに応じて、いくつかのヒントがあります。

キャッシュの最大サイズを定義し、キャッシュがいっぱいになった場合に古いアイテムをどう処理するか、清掃戦略を立て、キャッシュ内のオブジェクトの存続時間を決定し、キャッシュを他の場所に永続化できるかどうかを定義する必要があります。メモリ、アプリケーションの異常終了の場合、.。

3
Fabian Vilers

これは一般的な問題であり、アプリケーションのニーズに応じて多くの解決策があります。マイクロソフトがそれに対処するためにライブラリ全体をリリースしたことは非常に一般的です。独自のキャッシュをロールアップする前に、MicrosoftVelocityを確認する必要があります。 http://msdn.Microsoft.com/en-us/data/cc655792.aspx この助けを願っています。

3
mfawzymkh

WeakReferenceを使用することもできますが、オブジェクトがそれほど大きくない場合は、WeakReferenceがオブジェクト自体よりも多くのメモリを消費するため、これは適切な手法ではありません。また、オブジェクトがGCの第0世代から第1世代に到達しない短時間の使用である場合、WeakReferenceはあまり必要ありませんが、オブジェクトのIDisposableインターフェイスにはSuppressFinalizeでリリースします。

有効期間を制御する場合は、キャッシュ内のオブジェクトの日時/タイムスパンを再度更新するタイマーが必要です。

重要なことは、オブジェクトが大きい場合はWeakReferenceを選択し、そうでない場合は強参照を使用することです。また、ディクショナリに容量を設定し、一時ビンに追加のオブジェクトを要求するためのキューを作成して、オブジェクトをシリアル化し、ディクショナリに空きがあるときにロードして、一時ディレクトリからクリアすることもできます。

1
rob