Cache object with ObjectCache in .Net with expiry time

前端 未结 4 2313
死守一世寂寞
死守一世寂寞 2021-02-20 05:25

I am stuck in a scenario. My code is like below :

Update : its not about how to use data cache, i am already using it and its working , its about expanding it so

4条回答
  •  故里飘歌
    2021-02-20 05:50

    I have adapted the solution from Micro Caching in .NET for use with the System.Runtime.Caching.ObjectCache for MvcSiteMapProvider. The full implementation has an ICacheProvider interface that allows swapping between System.Runtime.Caching and System.Web.Caching, but this is a cut down version that should meet your needs.

    The most compelling feature of this pattern is that it uses a lightweight version of a lazy lock to ensure that the data is loaded from the data source only 1 time after the cache expires regardless of how many concurrent threads there are attempting to load the data.

    using System;
    using System.Runtime.Caching;
    using System.Threading;
    
    public interface IMicroCache
    {
        bool Contains(string key);
        T GetOrAdd(string key, Func loadFunction, Func getCacheItemPolicyFunction);
        void Remove(string key);
    }
    
    public class MicroCache : IMicroCache
    {
        public MicroCache(ObjectCache objectCache)
        {
            if (objectCache == null)
                throw new ArgumentNullException("objectCache");
    
            this.cache = objectCache;
        }
        private readonly ObjectCache cache;
        private ReaderWriterLockSlim synclock = new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);
    
        public bool Contains(string key)
        {
            synclock.EnterReadLock();
            try
            {
                return this.cache.Contains(key);
            }
            finally
            {
                synclock.ExitReadLock();
            }
        }
    
        public T GetOrAdd(string key, Func loadFunction, Func getCacheItemPolicyFunction)
        {
            LazyLock lazy;
            bool success;
    
            synclock.EnterReadLock();
            try
            {
                success = this.TryGetValue(key, out lazy);
            }
            finally
            {
                synclock.ExitReadLock();
            }
    
            if (!success)
            {
                synclock.EnterWriteLock();
                try
                {
                    if (!this.TryGetValue(key, out lazy))
                    {
                        lazy = new LazyLock();
                        var policy = getCacheItemPolicyFunction();
                        this.cache.Add(key, lazy, policy);
                    }
                }
                finally
                {
                    synclock.ExitWriteLock();
                }
            }
    
            return lazy.Get(loadFunction);
        }
    
        public void Remove(string key)
        {
            synclock.EnterWriteLock();
            try
            {
                this.cache.Remove(key);
            }
            finally
            {
                synclock.ExitWriteLock();
            }
        }
    
    
        private bool TryGetValue(string key, out LazyLock value)
        {
            value = (LazyLock)this.cache.Get(key);
            if (value != null)
            {
                return true;
            }
            return false;
        }
    
        private sealed class LazyLock
        {
            private volatile bool got;
            private T value;
    
            public T Get(Func activator)
            {
                if (!got)
                {
                    if (activator == null)
                    {
                        return default(T);
                    }
    
                    lock (this)
                    {
                        if (!got)
                        {
                            value = activator();
    
                            got = true;
                        }
                    }
                }
    
                return value;
            }
        }
    }
    

    Usage

    // Load the cache as a static singleton so all of the threads
    // use the same instance.
    private static IMicroCache stringCache = 
        new MicroCache(System.Runtime.Caching.MemoryCache.Default);
    
    public string GetData(string key)
    {
        return stringCache.GetOrAdd(
            key,
            () => LoadData(key),
            () => LoadCacheItemPolicy(key));
    }
    
    private string LoadData(string key)
    {
        // Load data from persistent source here
    
        return "some loaded string";
    }
    
    private CacheItemPolicy LoadCacheItemPolicy(string key)
    {
        var policy = new CacheItemPolicy();
    
        // This ensures the cache will survive application
        // pool restarts in ASP.NET/MVC
        policy.Priority = CacheItemPriority.NotRemovable;
    
        policy.AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(1);
    
        // Load Dependencies
        // policy.ChangeMonitors.Add(new HostFileChangeMonitor(new string[] { fileName }));
    
        return policy;
    }
    

    NOTE: As was previously mentioned, you are probably not gaining anything by caching a value that takes 100ms to retrieve for only 500ms. You should most likely choose a longer time period to hold items in the cache. Are the items really that volatile in the data source that they could change that quickly? If so, maybe you should look at using a ChangeMonitor to invalidate any stale data so you don't spend so much of the CPU time loading the cache. Then you can change the cache time to minutes instead of milliseconds.

提交回复
热议问题