MemoryCache를 지우는 방법?


100

MemoryCache 클래스를 사용하여 캐시를 만들었습니다. 몇 가지 항목을 추가했지만 캐시를 다시로드해야 할 때 먼저 지우고 싶습니다. 이를 수행하는 가장 빠른 방법은 무엇입니까? 모든 항목을 반복하여 한 번에 하나씩 제거해야합니까? 아니면 더 나은 방법이 있습니까?


1
.NET core의 경우이 답변을 확인 하십시오 .
Makla

답변:


61

Dispose 기존 MemoryCache를 만들고 새 MemoryCache 개체를 만듭니다.


3
처음에는 MemoryCache.Default를 사용하여 Dispose가 슬픔을 느끼게했습니다. 그래도 Dispose는 제가 찾을 수있는 최고의 솔루션이었습니다. 감사.
LaustN 2010

11
@LaustN MemoryCache.Default로 인한 "슬픔"에 대해 자세히 설명해 주시겠습니까? 현재 MemoryCache.Default를 사용하고 있습니다 ... MSDN의 MemoryCache 설명서를 보면 폐기 및 재생성이 권장되는지 궁금합니다. "필요하지 않으면 MemoryCache 인스턴스를 만들지 마십시오. 클라이언트 및 웹 응용 프로그램에서 캐시 인스턴스를 만들면 MemoryCache 인스턴스가 응용 프로그램 수명주기 초기에 생성됩니다. " .Default에 적용됩니까? Dispose를 사용하는 것이 잘못되었다고 말하는 것이 아니라 솔직히이 모든 것에 대한 설명을 찾고 있습니다.
ElonU Webdev 2010 년

8
현재 캐시 된 항목에 첨부 된 항목을 호출 하는 Dispose 것은 언급 할 가치가 있다고 생각했습니다 CacheEntryRemovedCallback.
Mike Guthrie

8
@ElonU : 다음 Stack Overflow 답변은 기본 인스턴스를 폐기 할 수있는 몇 가지 슬픔을 설명합니다. stackoverflow.com/a/8043556/216440 . 인용하자면 : "캐시 상태는 캐시가 삭제되었음을 나타내도록 설정되어 있습니다. 캐시 항목을 추가, 제거 또는 검색하는 메서드와 같이 캐시 상태를 변경하는 공용 캐싱 메서드를 호출하려고하면 예기치 않은 결과가 발생할 수 있습니다. 예를 들어 캐시가 삭제 된 후 Set 메서드를 호출하면 no-op 오류가 발생합니다. "
Simon Tewsi

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

구현은 캐시를 여러 Dictionary 개체로 분할하므로 열거자를 다시 전달하려면 모든 것을 단일 컬렉션으로 가져와야합니다. GetEnumerator에 대한 모든 호출은 위에서 설명한 전체 복사 프로세스를 실행합니다. 새로 생성 된 사전에는 원래 내부 키 및 값 개체에 대한 참조가 포함되어 있으므로 실제 캐시 된 데이터 값이 중복되지 않습니다.

문서의 경고가 정확합니다. LINQ 쿼리를 사용하는 위의 모든 답변을 포함하여 GetEnumerator ()를 사용하지 마십시오.

더 좋고 더 유연한 솔루션

다음은 기존 변경 모니터링 인프라를 기반으로하는 캐시를 효율적으로 지우는 방법입니다. 또한 전체 캐시 또는 명명 된 하위 집합을 지울 수있는 유연성을 제공하며 위에서 설명한 문제가 없습니다.

// 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-04-02

아주 좋아. 연결된 메모리 캐시 모니터 및 GUID를 사용하여 무언가를 구현하려고 시도했지만 기능을 강화하려고 시도함에 따라 약간 추악 해지기 시작했습니다.
Chao

7
일반적인 사용에는이 패턴을 권장하지 않습니다. 1. 느리고 구현상의 결함은 없지만 처리 방법은 매우 느립니다. 2. 만료 된 캐시에서 항목을 제거하는 경우 변경 모니터가 계속 호출됩니다. 3. 내 컴퓨터가 모든 CPU를 삼켜 서 성능 테스트를 실행할 때 캐시에서 30k 항목을 지우는 데 정말 오랜 시간이 걸렸습니다. 5 분 이상 기다린 후 몇 번 테스트를 종료했습니다.
Aaron M

1
@PascalMathys 불행히도 이것보다 더 좋은 해결책은 없습니다. 단점에도 불구하고 열거 형을 사용하는 것보다 여전히 더 나은 솔루션으로 사용했습니다.
Aaron M

9
@AaronM이 솔루션이 캐시를 폐기하고 새 캐시를 인스턴스화하는 것보다 여전히 낫습니까?
RobSiklos

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

1
개인적으로 저는 단위 테스트 [TestInitialize] 함수에서 이것을 사용하여 각 단위 테스트에 대한 메모리 캐시를 지우고 있습니다. 그렇지 않으면 캐시가 단위 테스트에서 지속되어 두 함수 간의 성능을 비교할 때 의도하지 않은 결과를 제공합니다.
야곱 모리슨

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
더 빠르거나 느린 지 확인하기 위해 테스트 했습니까?
Paul George

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.