MemoryCacheをクリアする方法は?


100

MemoryCacheクラスを使用してキャッシュを作成しました。いくつかのアイテムを追加しましたが、キャッシュをリロードする必要があるときは、最初にそれをクリアしたいと思います。これを行う最も速い方法は何ですか?すべてのアイテムをループして一度に1つずつ削除する必要がありますか、それとももっと良い方法がありますか?


1
.NETコアについては、この回答を確認してください。
マクラ2018年

回答:


61

Dispose 既存のMemoryCacheと新しいMemoryCacheオブジェクトを作成します。


3
私は最初にMemoryCache.Defaultを使用したため、Disposeにいくつかの悲しみを与えました。それでも、Disposeは私が見つけた最高のソリューションでした。ありがとう。
LaustN

11
@LaustNでは、MemoryCache.Defaultによって引き起こされる「悲しみ」について詳しく説明できますか?現在、MemoryCache.Defaultを使用しています... MSDNのMemoryCacheのドキュメントでは、破棄と再作成が推奨されているかどうか疑問に思っています:「必要でない限り、MemoryCacheインスタンスを作成しないでください。アプリケーションのライフサイクルの早い段階で作成されます。」これは.Defaultに適用されますか?Disposeの使用が間違っていると言っているのではなく、正直に言って、これらすべてについての明確化を求めています。
ElonU Webdev 2011年

8
現在のキャッシュされたアイテムにアタッチされたものを呼び出すことDispose 言及する価値があると思いましたCacheEntryRemovedCallback
マイクガスリー、

8
@ElonU:次のスタックオーバーフローの回答は、デフォルトインスタンスの破棄で遭遇する可能性があるいくつかの悲嘆を説明しています:stackoverflow.com/a/8043556/216440。引用すると、「キャッシュの状態は、キャッシュが破棄されることを示すように設定されています。キャッシュエントリを追加、削除、または取得するメソッドなど、キャッシュの状態を変更するパブリックキャッシュメソッドを呼び出そうとすると、予期しないことが発生する可能性があります。たとえば、キャッシュが破棄された後でSetメソッドを呼び出すと、no-opエラーが発生します。」
Simon Tewsi 2013年

56

列挙の問題

MemoryCache.GetEnumerator()は区間備考警告:「MemoryCacheインスタンスの列挙子を取得すると、リソースを大量に消費し、ブロッキングの操作であるため、列挙子が生産用途に使用されるべきではありません。」

GetEnumerator()実装の疑似コードで説明されている理由は次のとおりです。

Create a new Dictionary object (let's call it AllCache)
For Each per-processor segment in the cache (one Dictionary object per processor)
{
    Lock the segment/Dictionary (using lock construct)
    Iterate through the segment/Dictionary and add each name/value pair one-by-one
       to the AllCache Dictionary (using references to the original MemoryCacheKey
       and MemoryCacheEntry objects)
}
Create and return an enumerator on the AllCache Dictionary

実装はキャッシュを複数のディクショナリオブジェクトに分割するため、列挙子を返すためにすべてを1つのコレクションにまとめる必要があります。GetEnumeratorを呼び出すたびに、上記の完全なコピープロセスが実行されます。新しく作成されたディクショナリには、元の内部キーおよび値オブジェクトへの参照が含まれているため、実際にキャッシュされたデータ値は複製されません。

ドキュメントの警告は正しいです。GetEnumerator()は避けてください。LINQクエリを使用する上記のすべての回答が含まれます。

より優れた、より柔軟なソリューション

これは、既存の変更監視インフラストラクチャ上に単純に構築する、キャッシュをクリアする効率的な方法です。また、キャッシュ全体または名前付きサブセットのみをクリアする柔軟性があり、上記の問題はありません。

// By Thomas F. Abraham (http://www.tfabraham.com)
namespace CacheTest
{
    using System;
    using System.Diagnostics;
    using System.Globalization;
    using System.Runtime.Caching;

    public class SignaledChangeEventArgs : EventArgs
    {
        public string Name { get; private set; }
        public SignaledChangeEventArgs(string name = null) { this.Name = name; }
    }

    /// <summary>
    /// Cache change monitor that allows an app to fire a change notification
    /// to all associated cache items.
    /// </summary>
    public class SignaledChangeMonitor : ChangeMonitor
    {
        // Shared across all SignaledChangeMonitors in the AppDomain
        private static event EventHandler<SignaledChangeEventArgs> Signaled;

        private string _name;
        private string _uniqueId = Guid.NewGuid().ToString("N", CultureInfo.InvariantCulture);

        public override string UniqueId
        {
            get { return _uniqueId; }
        }

        public SignaledChangeMonitor(string name = null)
        {
            _name = name;
            // Register instance with the shared event
            SignaledChangeMonitor.Signaled += OnSignalRaised;
            base.InitializationComplete();
        }

        public static void Signal(string name = null)
        {
            if (Signaled != null)
            {
                // Raise shared event to notify all subscribers
                Signaled(null, new SignaledChangeEventArgs(name));
            }
        }

        protected override void Dispose(bool disposing)
        {
            SignaledChangeMonitor.Signaled -= OnSignalRaised;
        }

        private void OnSignalRaised(object sender, SignaledChangeEventArgs e)
        {
            if (string.IsNullOrWhiteSpace(e.Name) || string.Compare(e.Name, _name, true) == 0)
            {
                Debug.WriteLine(
                    _uniqueId + " notifying cache of change.", "SignaledChangeMonitor");
                // Cache objects are obligated to remove entry upon change notification.
                base.OnChanged(null);
            }
        }
    }

    public static class CacheTester
    {
        public static void TestCache()
        {
            MemoryCache cache = MemoryCache.Default;

            // Add data to cache
            for (int idx = 0; idx < 50; idx++)
            {
                cache.Add("Key" + idx.ToString(), "Value" + idx.ToString(), GetPolicy(idx));
            }

            // Flush cached items associated with "NamedData" change monitors
            SignaledChangeMonitor.Signal("NamedData");

            // Flush all cached items
            SignaledChangeMonitor.Signal();
        }

        private static CacheItemPolicy GetPolicy(int idx)
        {
            string name = (idx % 2 == 0) ? null : "NamedData";

            CacheItemPolicy cip = new CacheItemPolicy();
            cip.AbsoluteExpiration = System.DateTimeOffset.UtcNow.AddHours(1);
            cip.ChangeMonitors.Add(new SignaledChangeMonitor(name));
            return cip;
        }
    }
}

8
不足しているリージョン機能の実装のようです。
Jowen、2014

非常に素晴らしい。連鎖メモリキャッシュモニターとGUIDを使用して何かを実装しようとしましたが、機能を強化しようとしたときに少し醜くなり始めていました。
チャオ

7
このパターンは一般的な使用にはお勧めしません。1.その遅い、実装の障害はありませんが、disposeメソッドは非常に遅いです。2.期限切れのアイテムをキャッシュから削除する場合でも、変更モニターが呼び出されます。3.私のマシンはすべてのCPUを飲み込んでおり、パフォーマンステストを実行しているときに、キャッシュから3万個のアイテムをクリアするのに非常に長い時間がかかりました。5分以上待ってから数回、テストを終了しました。
アーロンM

1
@PascalMathys残念ながら、これよりも優れた解決策はありません。不利な点はありますが、列挙型を使用するよりも解決策が優れているため、結局使用しました。
アーロンM

9
@AaronMこのソリューションは、単にキャッシュを破棄して新しいキャッシュをインスタンス化するよりも優れていますか?
RobSiklos、2016年

35

http://connect.microsoft.com/VisualStudio/feedback/details/723620/memorycache-class-needs-a-clear-methodから

回避策は次のとおりです。

List<string> cacheKeys = MemoryCache.Default.Select(kvp => kvp.Key).ToList();
foreach (string cacheKey in cacheKeys)
{
    MemoryCache.Default.Remove(cacheKey);
}

33
ドキュメントから:MemoryCacheインスタンスの列挙子の取得は、リソースを大量に消費するブロック操作です。したがって、列挙子は実稼働アプリケーションでは使用しないでください。
TrueWill、

3
@emberdude列挙子を取得するのとまったく同じです-実装は何をしますSelect()か?
RobSiklos 2016年

1
個人的には、ユニットテスト[TestInitialize]関数でこれを使用して、各ユニットテストのメモリキャッシュをクリアしています。そうでない場合、キャッシュはユニットテスト全体で持続し、2つの関数のパフォーマンスを比較しようとすると、意図しない結果になります。
ジェイコブモリソン

6
@JacobMorrison間違いなく、ユニットテストは「本番アプリケーション」ではありません:)
Mels

1
@Melsは間違いなく、単体テストは「本番アプリケーション」と同じ標準で作成する必要があります。:)
Etherman


10

パフォーマンスが問題でない場合は、この素晴らしいワンライナーでうまくいきます。

cache.ToList().ForEach(a => cache.Remove(a.Key));


3

次のようなこともできます:


Dim _Qry = (From n In CacheObject.AsParallel()
           Select n).ToList()
For Each i In _Qry
    CacheObject.Remove(i.Key)
Next

3

これに出くわし、それに基づいて、もう少し効果的な並列のクリアなメソッドを書きました:

    public void ClearAll()
    {
        var allKeys = _cache.Select(o => o.Key);
        Parallel.ForEach(allKeys, key => _cache.Remove(key));
    }

1
それをテストして、それがより速い(または遅い)かどうかを確認しましたか?
ポールジョージ

1

c#GlobalCachingProviderを使用しているとき、私はキャッシュをクリアすることにのみ興味があり、これをオプションとして見つけました

                var cache = GlobalCachingProvider.Instance.GetAllItems();
                if (dbOperation.SuccessLoadingAllCacheToDB(cache))
                {
                    cache.Clear();
                }

0

magritte回答の少し改善されたバージョン。

var cacheKeys = MemoryCache.Default.Where(kvp.Value is MyType).Select(kvp => kvp.Key).ToList();
foreach (string cacheKey in cacheKeys)
{
    MemoryCache.Default.Remove(cacheKey);
}

0

MemoryCache.Defaultキャッシュを破棄し、プライベートフィールドシングルトンをnullに再設定して、MemoryCache.Defaultを再作成できます。

       var field = typeof(MemoryCache).GetField("s_defaultCache",
            BindingFlags.Static |
            BindingFlags.NonPublic);
        field.SetValue(null, null);
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.