C #: как реализовать умный кеш - PullRequest
19 голосов
/ 15 июля 2009

У меня есть несколько мест, где может быть полезна реализация кеша. Например, в случае выполнения поиска ресурсов на основе пользовательских строк, поиска имен свойств с использованием отражения или наличия только одного PropertyChangedEventArgs на имя свойства.

Простой пример последнего:

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, это означало бы, что мы в конечном итоге получим огромный кэш, который никогда не будет собирать мусор или что-то еще. Я представляю, что кешируются большие значения, и если приложение является долговременным, это может стать проблемой ... или как вы думаете? Как реализовать хороший кеш? Достаточно ли этого достаточно для большинства целей? Какие-нибудь примеры некоторых хороших реализаций кэша, которые не слишком сложны для понимания или слишком сложны для реализации?

Ответы [ 6 ]

25 голосов
/ 15 июля 2009

Это большая проблема, вам нужно определить область проблемы и применить правильные методы. Например, как бы вы описали срок действия объектов? Стали ли они за определенный промежуток времени? Они становятся несвежими от внешнего события? Как часто это происходит? Кроме того, сколько у вас объектов? Наконец, сколько стоит генерация объекта?

Самой простой стратегией было бы сделать прямое запоминание, как вы сделали выше. Это предполагает, что срок действия объектов никогда не истечет, и что их не так уж много, чтобы вы сохранили память и , так что вы думаете, что стоимость создания этих объектов оправдывает использование кеша для начала.

Следующим уровнем может быть ограничение количества объектов и использование неявной политики истечения срока действия, такой как LRU (используется в последнее время). Чтобы сделать это, вы обычно используете двусвязный список в дополнение к своему словарю, и каждый раз, когда к объекту обращаются, он перемещается в начало списка. Затем, если вам нужно добавить новый объект, но он превышает ваш предел общего количества объектов, вы удалите его из задней части списка.

Далее вам может потребоваться принудительное явное истечение срока действия, основанное на времени или внешнем стимуле. Это потребует, чтобы у вас было какое-то событие с истечением срока действия, которое можно было бы вызвать.

Как вы можете видеть, в кэшировании очень много дизайна, поэтому вам нужно понимать ваш домен и правильно разрабатывать. Я чувствовал, что вы не предоставили мне достаточно подробностей, чтобы обсудить конкретные вопросы.

P.S. Пожалуйста, подумайте об использовании Generics при определении вашего класса, чтобы можно было хранить много типов объектов, что позволяет повторно использовать ваш код кэширования.

18 голосов
/ 15 июля 2009

Вы можете обернуть каждый из ваших кэшированных элементов в WeakReference. Это позволит GC восстанавливать элементы, если и когда это потребуется, однако не дает детального контроля того, когда элементы исчезнут из кэша, или позволяет реализовать явные политики истечения срока действия и т. Д.

(Ха! Я только что заметил, что пример, приведенный на странице MSDN , является простым классом кэширования.)

9 голосов
/ 01 ноября 2011

Похоже, что .NET 4.0 теперь поддерживает System.Runtime.Caching для кеширования многих типов вещей. Вы должны изучить это в первую очередь, а не заново изобретать колесо. Подробнее:

http://msdn.microsoft.com/en-us/library/system.runtime.caching%28VS.100%29.aspx

3 голосов
/ 20 июля 2009

Это распространенная проблема, которая имеет много решений в зависимости от потребностей вашего приложения. Это так часто, что Microsoft выпустила целую библиотеку для решения этой проблемы. Вы должны проверить Microsoft Velocity, прежде чем свернуть свой кеш. http://msdn.microsoft.com/en-us/data/cc655792.aspx Надеюсь, что это поможет.

3 голосов
/ 15 июля 2009

Это хорошая дискуссия, но в зависимости от вашего приложения, вот несколько советов:

Вы должны определить максимальный размер кэша, что делать со старыми элементами, если ваш кэш заполнен, иметь стратегию очистки, определить время жизни объекта в кэше, может ли ваш кэш быть сохранен где-то еще эта память, в случае аварийного завершения приложения, ...

1 голос
/ 14 февраля 2011

Вы можете использовать WeakReference, но если ваш объект не такой большой, как не потому, что WeakReference будет занимать больше памяти, чем сам объект, что не является хорошей техникой. Кроме того, если объект используется в течение короткого времени, когда он никогда не попадет в поколение 1 из поколения 0 в GC, нет особой необходимости в WeakReference, но интерфейс IDisposable для объекта будет иметь место при выпуске на SuppressFinalize.

Если вы хотите контролировать время жизни, вам нужен таймер, чтобы снова обновить datetime / timespan требуемогоExpirationTime для объекта в вашем кэше.

Важно то, что если объект большой, выберите WeakReference, в противном случае используйте сильную ссылку. Кроме того, вы можете установить емкость в Словаре и создать очередь для запроса дополнительных объектов в вашей временной корзине, сериализовав объект и загружая его, когда в Словаре есть место, а затем очистите его из временного каталога.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...