2009-07-15 73 views
19

我有一些實施某種緩存可能會有用的地方。例如,在執行基於自定義字符串的資源查找的情況下,使用反射查找屬性的名稱,或者每個屬性名稱只有一個PropertyChangedEventArgsC#:如何實現智能緩存

最後一個一個簡單的例子:

public static class Cache 
{ 
    private static Dictionary<string, PropertyChangedEventArgs> cache; 
    static Cache() 
    { 
     cache = new Dictionary<string, PropertyChangedEventArgs>(); 
    } 
    public static PropertyChangedEventArgs GetPropertyChangedEventArgs(
     string propertyName) 
    { 
     if (cache.ContainsKey(propertyName)) 
      return cache[propertyName]; 

     return cache[propertyName] = new PropertyChangedEventArgs(propertyName); 
    } 
} 

但是,將這項工作呢?例如,如果我們有一個不同的propertyNames的整個負載,這將意味着我們最終會有一個巨大的緩存坐在那裏永遠不會被垃圾收集或任何東西。我在想象如果緩存的是更大的值,並且如果應用程序是長期運行的應用程序,這可能最終會成爲一個問題......或者您認爲什麼?一個好的緩存應該如何實現?這對於大多數目的來說足夠好嗎?一些好的緩存實現的任何例子都不太難理解,或者實現過於複雜?

+12

「*只有兩種計算機科學堅硬的東西:緩存失效和命名的事情*」菲爾 - Karlton – JulianR 2011-11-01 20:19:33

+1

這是不值得一個整體的答案,但這個特定的實現會在多線程環境中出現失敗。在最低限度,你需要鎖定`GetPropertyChangedEventArgs()`中對`cache`的訪問。 只是試圖幫助任何人來到這裏,並複製麪食。 – aaronburro 2016-12-09 16:45:25

回答

18

你可以在一個WeakReference包裝每個緩存的項目。這將允許GC在需要時回收物品,但是它不會對物品何時從緩存中消失或允許您實施明確的到期政策等進行細粒度控制。

(Ha !我只注意到在MSDN page上給出的例子是一個簡單的緩存類。)

3

這是一個很好的辯論有,但根據您的應用程序,這裏的一些小技巧:

你應該定義緩存的最大尺寸,做什麼用的舊項目,如果你的緩存已滿,有清除戰略,確定一個時間住在緩存中的對象,你的緩存可以/必須在其他地方依然存在內存中,應用程序異常終止的情況下,...

+1

什麼是清除策略? – Svish 2009-07-15 13:45:46

+4

清理算法負責從緩存中移除項目以釋放​​緩存存儲資源。 – 2009-07-15 14:08:18

+0

啊,好的。這就說得通了。 – Svish 2009-07-15 14:18:00

24

這是一個很大的問題,你需要確定問題的領域並應用正確的技術。例如,你會如何描述對象的到期?它們是否在固定的時間間隔內變得陳舊?它們是否因外部事件而變得陳舊?發生這種情況的頻率如何?另外,你有多少個物體?最後,生成對象需要多少成本?

最簡單的策略是做直記憶化,如你有以上。這假設對象永不過期,並且沒有太多內存運行你的內存幹,你認爲創建這些對象的成本需要使用緩存開始。

下一層可以是限制對象的數目,並且使用隱式過期策略,諸如LRU(最近最少使用)。要做到這一點,除了字典之外,通常還要使用雙向鏈表,並且每次訪問對象時都會將其移到列表的前面。然後,如果你需要添加一個新的對象,但它超過了你所有對象的限制,你可以從列表的後面移除。

接下來,您可能需要執行顯式過期,無論是基於時間,或一些外部刺激。這將要求你有某種可能被調用的過期事件。

正如你可以看到有很多的緩存設計,所以你需要適當地瞭解您的域名和工程師。我覺得你沒有提供足夠的細節來討論具體細節。

P.S.請在定義類時考慮使用泛型,以便可以存儲多種類型的對象,從而允許重用您的高速緩存代碼。

+2

+1提及很多因素並提出基本解決方案。 – 2009-07-15 13:37:59

3

這是一個常見問題,它有許多解決方案,具體取決於您的應用需求。 微軟發佈了一個全圖書館來解決這個問題非常普遍。 在上傳您自己的緩存之前,您應該查看Microsoft Velocity。 http://msdn.microsoft.com/en-us/data/cc655792.aspx 希望得到這個幫助。

1

你可以使用WeakReference但是如果你的對象不是那麼大,因爲WeakReference會比對象本身佔用更多的內存,這不是一個好技術。另外,如果對象是短時間的使用情況,它不會在GC上從第0代開始生成第1代,則WeakReference的需求不大,但對象上的IDisposable接口將與SuppressFinalize上的版本一起使用。

如果您想要控制生命週期,您需要一個計時器來再次更新日期時間/時間跨度,然後再對緩存中的對象執行desiredExpirationTime。

重要的是,如果對象很大,那麼選擇WeakReference,否則使用強引用。此外,您可以設置Dictionary上的容量並創建一個隊列,用於請求臨時文件夾中的其他對象序列化對象,並在Dictionary中有空間時加載它,然後從臨時目錄中清除它。