2010-12-03 40 views
6

我想用.NETLazy<T>類來實現線程安全緩存。假設我們有以下設置:可以(應該?)懶惰<T>被用作緩存技術嗎?

class Foo 
{ 
    Lazy<string> cachedAttribute; 

    Foo() 
    { 
     invalidateCache(); 
    } 

    string initCache() 
    { 
     string returnVal = ""; 
     //CALCULATE RETURNVAL HERE 
     return returnVal; 
    } 

    public String CachedAttr 
    { 
     get 
     { 
      return cachedAttribute.Value; 
     } 
    } 

    void invalidateCache() 
    { 
     cachedAttribute = new Lazy<string>(initCache, true); 
    } 
} 

我的問題是:

  1. 將這項工作呢?
  2. 鎖定將如何工作?

我覺得我錯過了一個鎖定在invalidateCache附近的地方,但對於我的生活我無法弄清楚它是什麼。

我確定這個地方有問題,我只是沒有弄清楚在哪裏。

[編輯]

好吧,但是它看起來像我是對的,有些事情我還沒有想過。如果一個線程看到過時的緩存會是一件非常糟糕的事情,所以看起來像「懶惰」不夠安全。該屬性雖然被訪問了很多,但是我正在進行預先優化,希望能夠學習一些東西,並且有一個模式可以在將來用於線程安全緩存。我會繼續努力的。

P.S .:我決定讓對象線程不安全並且可以小心控制對象的訪問權限。

回答

8

嗯,它不是線程安全的,因爲第一個線程可能沒有看到cachedAttribute的更改,因爲一個線程仍然可以在另一個線程在無效後看到新值之後看到舊值。從理論上說,這種情況可能會延續一輩子,雖然這是件不可思議的事:)

使用Lazy<T>不變值緩存似乎是一個更好的主意,我 - 更符合它是如何打算 - 但如果你能應對在另一個線程中長時間使用舊的「無效」值的可能性,我認爲這樣可以。

+0

是的,無效值在這種情況下不好(至少不是任意長的值)。謝謝。 – Crisfole 2010-12-03 18:10:27

3

cachedAttribute是一種共享資源,需要對其進行保護,以免發生併發修改。

lock保護它:

private readonly object gate = new object(); 

public string CachedAttr 
{ 
    get 
    { 
     Lazy<string> lazy; 
     lock (gate)       // 1. Lock 
     { 
      lazy = this.cachedAttribute; // 2. Get current Lazy<string> 
     }         // 3. Unlock 
     return lazy.Value     // 4. Get value of Lazy<string> 
              // outside lock 
    } 
} 

void InvalidateCache() 
{ 
    lock (gate)        // 1. Lock 
    {          // 2. Assign new Lazy<string> 
     cachedAttribute = new Lazy<string>(initCache, true); 
    }          // 3. Unlock 
} 

或使用Interlocked.Exchange

void InvalidateCache() 
{ 
    Interlocked.Exchange(ref cachedAttribute, new Lazy<string>(initCache, true)); 
} 

volatile可能在這種情況下工作,以及,但它使我的頭不疼。

+0

我會建議把`cachedAttribute`的檢索放在鎖中,但是讓`Value`的評估出現在鎖的外面。 – 2010-12-03 18:11:02