2014-06-08 22 views
1

我有代碼,當前沒有線程安全的一個故事:與ASP.Net緩存保證線程安全 - 兩種策略

public byte[] GetImageByteArray(string filepath, string contentType, RImgOptions options) 
{    
    //Our unique cache keys will be composed of both the image's filepath and the requested width 
    var cacheKey = filepath + options.Width.ToString(); 
    var image = HttpContext.Current.Cache[cacheKey]; 

    //If there is nothing in the cache, we need to generate the image, insert it into the cache, and return it 
    if (image == null) 
    { 
     RImgGenerator generator = new RImgGenerator(); 
     byte[] bytes = generator.GenerateImage(filepath, contentType, options); 
     CacheItem(cacheKey, bytes); 
     return bytes; 
    } 
    //Image already exists in cache, serve it up! 
    else 
    { 
     return (byte[])image; 
    } 
} 

CacheItem()方法檢查,看看它的最大緩存大小已經達成,並如果有,它會開始刪除緩存的項目:

//If the cache exceeds its max allotment, we will remove items until it falls below the max 
while ((int)cache[CACHE_SIZE] > RImgConfig.Settings.Profile.CacheSize * 1000 * 1000) 
{ 
    var entries = (Dictionary<string, DateTime>)cache[CACHE_ENTRIES]; 
    var earliestCacheItem = entries.SingleOrDefault(kvp => kvp.Value == entries.Min(d => d.Value)); 
    int length = ((byte[])cache[earliestCacheItem.Key]).Length; 
    cache.Remove(earliestCacheItem.Key); 
    cache[CACHE_SIZE] = (int)cache[CACHE_SIZE] - length; 
} 

由於另一個線程引用它的一個線程可以從緩存中刪除一個項目,我能想到的兩個選項:

選項1:鎖定

lock (myLockObject) 
{ 
    if(image == null){ **SNIP** } 
} 

選項2:指定一個淺拷貝到一個局部變量

var image = HttpContext.Current.Cache[cacheKey] != null ? HttpContext.Current.Cache[cacheKey].MemberwiseClone() : null; 

這兩種選擇都有開銷。第一個強制線程一次輸入一個代碼塊。第二個需要在內存中創建一個可能不重要的新對象。

我可以在這裏聘請其他任何策略嗎?

+0

我沒有看到這個代碼的問題。據我所知,從緩存中刪除項目不會處理它。因此,從緩存中刪除它只會刪除引用。引用它的對象應該不受影響,除非您明確地處理它。Cache本身是線程安全的,所以在查找時不必擔心被刪除的對象。 –

+0

你也知道你可以在你的web.config中設置緩存的最大大小?當達到這個大小時,asp.net本身就會開始修剪緩存?我不知道爲什麼你覺得自己需要這樣做... –

+0

@ErikFunkenbusch我相信你說的是真實的,本地'image'變量將持有一個引用,使緩存指向的對象不符合條件爲垃圾收集。我在看一個沒有使用局部變量(但可能應該是)的例子,而是在兩個單獨的操作中重新查詢緩存,因此使用了鎖。無論如何,謝謝你明確表示我不需要兩個不吃人吃的選項。 –

回答

0

爲了提供緩存解決方案的純粹一致性,您應該在緩慢應用程序的同時鎖定資源。

一般來說,您應該嘗試根據您的應用程序邏輯提供緩存策略。

  • 檢查滑動窗口緩存:雖然這是舊的,當一些 時間跨度項 - 將減少不同的線程鎖定 - 好,當你 具有不肯定會 再次使用不同的緩存項目的大傳播。
  • 考慮使用最常用的策略:當達到最大高速緩存大小應刪除是最少使用 項目 - 最好的服務,而 您有多個客戶端打的緩存 內容經常同一部分。

只要檢查哪一套更適合您的BL類型並使用它。它根本不會消除鎖定問題,但正確的選擇將顯着消除賽車條件。

爲了減少不同線程之間的共享資源,請在每個項目上使用讀寫鎖定,而不是在整個集合上使用讀寫鎖定。這也會提升你的表現。

應該牢記的另一個考慮因素 - 如果具有相同路徑的圖像內容在磁盤上物理上發生變化(不同的圖像使用相同名稱保存)錯誤地提供不相關的數據。

希望它有幫助。