2009-06-29 121 views
1

我正在C#中實現一個簡單的緩存,並試圖使其可以從多個線程訪問。在基本閱讀的情況下,很容易:鎖定自加載緩存

var cacheA = new Dictionary<int, MyObj>(); // Populated in constructor 

public MyObj GetCachedObjA(int key) 
{ 
    return cacheA[key]; 
} 

這是,我相信,完全是線程安全的。但我也想讓它自動加載。也就是說,如果緩存未在訪問時填充,則在滿足訪問請求之前將其填充。

Dictionary<int, MyObj> cacheB = null; 

public MyObj GetCachedObjB(int key) 
{ 
    if (cacheB == null) 
    { 
     PopulateCacheB(); 
    } 
    return cacheB[key]; 
} 

private void PopulateCacheB() 
{ 
    cacheB = new Dictionary<int, MyObj>(); 
    foreach (MyObj item in databaseAccessor) 
    { 
     cacheB.Add(item.Key, item); 
    } 
} 

這不是線程安全的,因爲cacheB實例化後,一個線程可以訪問GetCachedObjB但在此之前它是完全填充,如果另一個線程是填充它的過程。 那麼在cacheB上執行鎖定以便緩存是線程安全的最佳方式是什麼?

回答

1

可以使用lock語句簡單的線程安全:

private Dictionary<int, MyObj> cacheB = null; 
private readonly object cacheLockB = new object(); 

public MyObj GetCachedObjB(int key) 
{ 
    lock (cacheLockB) 
    { 
     if (cacheB == null) 
     { 
      Dictionary<int, MyObj> temp = new Dictionary<int, MyObj>(); 
      foreach (MyObj item in databaseAccessor) 
      { 
       temp.Add(item.Key, item); 
      } 
      cacheB = temp; 
     } 
     return cacheB[key]; 
    } 
} 

如果你需要比lock擠出更多的性能允許,那麼你可以使用一個ReaderWriterLockSlim代替,這將允許多個線程讀取字典同時。當您需要填充或更新字典時,可以將鎖升級到寫入模式。

0

您可以使用線程安全的Enterprise Library Cache。

1

在監視器中包裝填充操作。這樣,如果另一個線程在填充緩存時嘗試讀取緩存,那麼在它嘗試讀取之前,線程將被迫等待填充操作完成/錯誤。

您也可以做到這一點的讀取停止,而你從中讀取被修改的高速緩存,在這種情況下,你需要讀入一個臨時變量,釋放監視器,然後返回變量或你會遇到鎖定問題。

public MyObj GetCachedObjB(int key) 
{ 
    try { 
     Monitor.Enter(cacheB); 

     if (cacheB == null) 
     { 
      PopulateCacheB(); 
     } 
    } finally { 
     Monitor.Exit(cacheB); 
    } 
    return cacheB[key]; 
} 

作爲一般記它,你不妨做時添加一些關鍵的驗證讀取你的字典,雖然這取決於你是否要對不存在的密鑰或一些默認值的誤差。

+0

任何不使用「鎖定」語句而不是所有「嘗試...輸入...終於...退出」的東西的原因? http://msdn.microsoft.com/en-us/library/c5kehkcz.aspx – LukeH 2009-06-30 17:09:59

1

假設你的詞典是線程安全的或只讀人口後,你可以在這樣一個線程安全的方式來填充它:

private void PopulateCacheB() 
{  
    Dictionary<int, MyObj>() dictionary = new Dictionary<int, MyObj>();  
    foreach (MyObj item in databaseAccessor)  
    {   
     dictionary.Add(item.Key, item);  
    } 
    cacheB = dictionary; 
} 

在最壞情況下的數據將來自「databaseAccessor」檢索超過一旦出現競賽狀況,但這不應該受到傷害。