Простой кэш в памяти

от автора

Добрый день!

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

За примером реализации прошу под кат.

Реализация

Общая идея такая: кэш должен быть удобным и прозрачным в использовании. Я вдохновился интерфейсом класса ThreadLocal. Поэтому в конструкторе требуется фабричный метод для получения исходных значений, а потом они уже кэшируются. Опять же, в моём случае, я посчитал вполне логичным держать данные на слабых ссылках (WeakReference), таким образом, пока данные нужны, кто-то их использует, они будут доступны. Дальше, по мере выделения новой памяти, данные из кэша будут вытесняться.
Есть всего два метода: индексатор, который вернёт данные либо из источника, либо из кэша.
А также метод CleanCache, чтобы очищать словарь от мёртвых ссылок и таким образом устранить утечку памяти. В реальных условиях далеко не всегда это необходимо. Например, в моих условиях новые записи в кэше будут появляться всего несколько раз в день, так что даже за годы работы ссылки не сожрут сколько-то значительный объём памяти.

Цель публикации

В основном, мне интересно услышать мнение о моей реализации, как бы вы сделали, есть ли ошибки. Ну и если кому-то это поможет — берите, используйте 🙂

Исходник
public class WeakCache<TKey,TValue> where TValue : class     {         const int cacheCleanInterval = 60;         private readonly Func<TKey, TValue> getter;         private readonly Dictionary<TKey, WeakReference> data = new Dictionary<TKey, WeakReference>();         private readonly ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();         private DateTime lastCacheClean = DateTime.MinValue;                  public WeakCache(Func<TKey,TValue> getter)         {             this.getter = getter;         }          public TValue this[TKey key]         {             get             {                 CleanCache();                 try                 {                     rwLock.EnterUpgradeableReadLock();                     WeakReference wr;                     TValue val;                     if (data.TryGetValue(key, out wr))                     {                         val = (TValue)wr.Target;                         if (val != null)                             return val;                     }                     try                     {                         rwLock.EnterWriteLock();                         if (data.TryGetValue(key, out wr))                         {                             val = (TValue)wr.Target;                             if (val != null)                                 return val;                         }                         data[key] = new WeakReference(val = getter(key));                         return val;                     }                     finally                     {                         rwLock.ExitWriteLock();                     }                 }                 finally                 {                     rwLock.ExitUpgradeableReadLock();                 }             }         }          void CleanCache()         {             if ((DateTime.Now - lastCacheClean).TotalSeconds > cacheCleanInterval)             {                 try                 {                     rwLock.EnterWriteLock();                     if ((DateTime.Now - lastCacheClean).TotalSeconds > cacheCleanInterval)                     {                         lastCacheClean = DateTime.Now;                         var refs = data.ToArray();                         foreach (var weakReference in refs)                         {                             if (!weakReference.Value.IsAlive)                                 data.Remove(weakReference.Key);                         }                     }                 }                 finally                  {                     rwLock.ExitWriteLock();                 }             }         }     } 

ссылка на оригинал статьи http://habrahabr.ru/post/185616/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *