2009-07-15 1 views
19

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

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

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); 
    } 
} 

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

+12

«* Есть только два жестких вещей в области компьютерных наук:. Кэш недействительности и называющие вещи *» - Фил Karlton – JulianR

+1

Это не стоит весь ответ, но это конкретная реализация будет неэффективно выглядят в многопоточной среде. По минимальному минимуму вам нужно заблокировать доступ к 'cache' в' GetPropertyChangedEventArgs() '. Просто пытаюсь помочь любому, кто приходит и копирует это. – aaronburro

ответ

18

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

(Ha Я просто заметил, что пример, приведенный на MSDN page, является простым классом кеширования.)

+1

Круто! Что именно делает WeakReference в этом случае? – Svish

+1

На этой странице есть хорошее объяснение: http://msdn.microsoft.com/en-us/library/ms404247.aspx – LukeH

+4

На практике я нахожу, что .NET GC настолько эффективен/агрессивен, что слабые ссылки не зависают вокруг, что означает, что они действительно подходят для данных, которые недолговечны и/или недороги для воссоздания. – LukeH

3

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

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

+1

Что такое стратегия удаления? – Svish

+4

Алгоритм очистки является причиной удаления элементов из кеша в свободные ресурсы хранения кеша. –

+0

А, ок. В этом есть смысл. – Svish

24

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

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

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

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

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

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

+2

+1 для упоминания множества факторов и предложения основных решений. –

+0

Я как бы отказался от spesifics только потому, что я надеялся, что получу более общие советы о том, что думать и т. Д. Так что спасибо за это :) При использовании дженериков вы имеете в виду обменять кеш с кэшем и поменять местами все PropertyChangedEventArgs с T также? Или что-то более умное? Не все другие объекты имеют смысл идентифицировать по строкам, которые я бы подумал ... – Svish

+1

Просто базовые генераторы да, вы можете использовать тот же шаблон, что и словарь , и позволить пользователю вашего кеша определить тип K и V. –

3

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

+0

+1 - Эта библиотека похожа на ее корни в блоке приложений кэширования из корпоративной библиотеки. –

+0

Который теперь называется «Службой кэширования AppFabric». Здесь отсутствует два ключевых слова: переносимость и бесплатный. Почему Microsoft не может просто выпускать что-то как стандартную часть .NET? –

1

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

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

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

9

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

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

+4

За исключением того, что в качестве ключа используется String, а Object - для значения карты! Какое разочарование, как они могут реализовать общий API, такой как кеш, и не использовать дженерики для типа ключа/объекта? – marq

+0

Ну, я могу понять, используя String для ключа. Я не знаю каких-либо хеш-объектов, которые по крайней мере не переносят ключ в вещь на основе String. И не является ли объект просто универсальным контейнером? –

+0

@BrendanByrd У каждого объекта уже есть метод хеш-кода. – Casey

 Смежные вопросы

  • Нет связанных вопросов^_^