使用通用基类中的静态对象缓存锁定

时间:2012-05-18 08:32:50

标签: c# generics caching

我有一个用于值缓存功能的通用基类。

public abstract class CachedValueProviderBase<T> : ICachedValueProvider<T> where T : class
{
    private Cache Cache { set; get; }
    protected string CacheKey { get; set; }
    protected int CacheSpanInMinutes { get; set; }

    private static readonly object _cacheLock = new object();

    public T Values
    {
        get
        {
            T value = Cache[CacheKey] as T;
            if (value == null)
            {
                lock (_cacheLock)
                {
                    value = Cache[CacheKey] as T;
                    if (value == null)
                    {
                        value = InitializeCache();
                    }
                }
            }

            return value;
        }
    }

    protected CachedValueProviderBase()
    {
        Cache = HttpRuntime.Cache;
        CacheSpanInMinutes = 15;
    }

    public T CacheValue(T value)
    {
        if (value != null)
        {
            lock (_cacheLock)
            {
                Cache.Insert(CacheKey, value, null, DateTime.UtcNow.AddMinutes(CacheSpanInMinutes),
                             Cache.NoSlidingExpiration);
            }
        }

        return value;
    }

    private T InitializeCache()
    {
        T value = Initialize();
        CacheValue(value);

        return value;
    }

    protected abstract T Initialize();
}

我有几个使用这个基类的类,只要T不同就可以了。例如,当两个子类使用相同的T,字符串时,它们共享相同的缓存锁定对象。在基类中实现逻辑但是仍然为每个子类提供自己的缓存锁对象的最佳方法是什么?

更新 在下面的建议之后,我更新了我的课程:

public abstract class CachedValueProviderBase<T> : ICachedValueProvider<T> where T : class
    {
        private Cache Cache { set; get; }
        protected string CacheKey { get; set; }
        protected int CacheSpanInMinutes { get; set; }
        private object _cacheLock = new object();

        public T Values
        {
            get
            {
                T value = Cache[CacheKey] as T;
                if (value == null)
                {
                    lock (_cacheLock)
                    {
                        value = Cache[CacheKey] as T;
                        if (value == null)
                        {
                            value = InitializeCache();
                        }
                    }
                }

                return value;
            }
        }

        protected CachedValueProviderBase()
        {
            Cache = HttpRuntime.Cache;
            CacheSpanInMinutes = 15;
        }

        public T CacheValue(T value)
        {
            if (value != null)
            {
                Cache.Insert(CacheKey, value, null, DateTime.UtcNow.AddMinutes(CacheSpanInMinutes),
                             Cache.NoSlidingExpiration);

            }

            return value;
        }

        private T InitializeCache()
        {
            T value = Initialize();
            CacheValue(value);

            return value;
        }

        protected abstract T Initialize();
    }
}

我的子类现在是单例,所以我可以摆脱静态缓存锁定对象,使其成为一个实例变量。

3 个答案:

答案 0 :(得分:1)

好吧,只需删除cacheLock对象上的static修饰符即可。

该关键字强制在共享相同通用参数类型的所有子类实例之间共享该字段。

如果删除它,则无论泛型参数的类型如何,cacheLock对象都将专用于子类的每个实例

 private static readonly object _cacheLock = new object();

应该是:

 private readonly object _cacheLock = new object();

希望有所帮助

答案 1 :(得分:1)

我必须仔细查看您的代码,以确定它是否正确。一旦我发现你的缓存是HttpRuntime.Cache,它就有意义了。 HttpRuntime.Cache是线程安全的。否则你会遇到一些线程安全问题。根据您当前的代码,我建议您执行以下操作:

private string CacheKey { get; set; }

protected CachedValueProviderBase(string cacheKey)
{
    this.CacheKey = cacheKey + "_" + typeof(T).FullName;
}

通过提供cacheKey作为构造函数参数并使属性成为私有(或只读取),可以防止以后更改它。通过将类型名称附加到键,可以防止缓存冲突,因为每个人都在使用相同的缓存。

最后一点。 lock方法中的CacheValue是多余的,因为Cache是线程安全的。

答案 2 :(得分:0)

我通过在我的基类GetCacheLockObject()中实现一个抽象方法来处理这个问题。

protected abstract object GetCacheLockObject();

然后,每个派生类都返回自己对缓存锁对象的引用:

private static readonly object _cacheLockObject = new Object();

protected override object GetCacheLockObject()
{
    return _cacheLockObject;
}

调用锁定共享基类缓存代码然后引用此方法而不是基类中的对象。