2012-05-18 29 views
0

我有一個用於值緩存功能的通用基類。通用基類中的靜態對象的緩存鎖定

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(); 
    } 
} 

我的子類現在單身,所以我可以擺脫靜態cachelock對象使其成爲一個實例變量。

回答

1

那麼,只需刪除你的cacheLock對象的靜態修飾符。

該關鍵字強制該字段在共享相同通用參數類型的所有子類實例之間共享。

如果將其刪除,則無論泛型參數的類型如何,cacheLock對象都將被私有到子類的每個實例

private static readonly object _cacheLock = new object(); 

應該是:

private readonly object _cacheLock = new object(); 

希望幫助

+0

這是運行在網絡服務器上,所以我仍然必須鎖定多個線程的緩存。除去靜態時,可能會有多個線程初始化緩存。 – b3n

+0

然後你應該考慮使用[singleton pattern](http://en.wikipedia。org/wiki/Singleton_pattern),以便您擁有單個緩存,而不管訪問它的線程如何,同時爲每個緩存實例保留唯一的緩存鎖定。 –

+0

@ b3n如果您使用.net 4.0,則可以使用'Lazy '進行線程安全的ininitalization。無論如何,實例方法的靜態鎖是**糟糕的事情。 – undefined

1

我不得不把你的代碼好好看看,來看看它是正確的。一旦我注意到你的緩存是HttpRuntime.Cache,這是有道理的。 HttpRuntime.Cache是線程安全的。否則,你會遇到幾個線程安全問題。根據您當前的,代碼我建議你做到以下幾點:

private string CacheKey { get; set; } 

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

通過爲構造函數的參數供應cacheKey並使私有財產(或只讀會做),你防止它被後來上被改變。通過將類型名稱附加到密鑰上,可以防止緩存衝突,因爲每個人都使用相同的緩存。

最後一個註釋。 CacheValue方法中的lock是冗餘的,因爲Cache是線程安全的。

+0

好的!我錯過了這一點...... –

+0

所以我可以基本上擺脫緩存鎖對象看到緩存是線程安全的? – b3n

+0

基本上,是的:) –

0

我通過在我的基類GetCacheLockObject()中實現一個抽象方法來處理這個問題。

protected abstract object GetCacheLockObject(); 

每個派生類然後返回其自己的參考到緩存鎖定對象:

private static readonly object _cacheLockObject = new Object(); 

protected override object GetCacheLockObject() 
{ 
    return _cacheLockObject; 
} 

呼叫在共享基礎類緩存代碼鎖定然後引用該方法,而不是在基類的對象。