2016-12-07 11 views
2

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

Мой класс выглядит примерно так:

public class DataProvider 
{ 
    private ConcurrentDictionary<string, byte[]> dataCache; 
    private int dataCacheSize; 
    private int maxDataCacheSize; 
    private object dataCacheSizeLockObj = new object(); 

    public DataProvider(int maxCacheSize) 
    { 
     maxDataCacheSize = maxCacheSize; 
     dataCache = new ConcurrentDictionary<string,byte[]>(); 
    } 

    public byte[] GetData(string key) 
    { 
     byte[] retVal; 

     if (dataCache.ContainsKey(key)) 
     { 
      retVal = dataCache[key]; 
     } 
     else 
     { 
      retVal = ... // get data from somewhere else 

      if (dataCacheSize + retVal.Length <= maxDataCacheSize) 
      { 
       lock (dataCacheSizeLockObj) 
       { 
        dataCacheSize += retVal.Length; 
       } 
       dataCache[key] = retVal; 
      } 
     } 
     return retVal; 
    } 
} 

Моей проблема: как я могу убедиться, что dataCacheSize всегда имеют правильное значение? Если два потока одновременно запросят одни и те же нераскрытые данные, они оба будут записывать свои данные в кэш, что не представляет проблемы, поскольку данные одинаковы, а второй поток просто перезаписывает кэшированные данные теми же данными. Но откуда я могу узнать, если он был перезаписан или нет, чтобы не считать его размер дважды?

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

Есть элегантный способ выполнить эту задачу без добавления сложных механизмов запирающих?

+4

Вместо того, чтобы пытаться кэшировать «рулон», взгляните на System.Runtime.Caching.MemoryCache. https://msdn.microsoft.com/en-us/library/system.runtime.caching.memorycache(v=vs.110).aspx – Kevin

+0

Хорошо, MemoryCache, похоже, выполняет эту работу. Однако я должен проверить параметры управления кешем. Вы хотите ответить на вопрос? Я не могу принять комментарий. – Ben

+0

Добавлено ответ. Спасибо – Kevin

ответ

1

Вместо того, чтобы пытаться «ролл у вас есть» кэширование, посмотрите на System.Runtime.Caching. MemoryCache. См. Комментарий выше.

0

Поскольку вы обновляете dataCacheSize внутри замка, вы можете просто проверить здесь, если он останется верным:

if (dataCacheSize + retVal.Length <= maxDataCacheSize) 
{ 
    lock (dataCacheSizeLockObj) 
    { 
     if (dataCacheSize + retVal.Length > maxDataCacheSize) 
     { 
      return retVal; 
     } 
     dataCacheSize += retVal.Length; 
    } 
    byte[] oldVal = dataCache.GetOrAdd(key, retVal); 
    if (oldVal != retVal) 
    { 
     // retVal wasn't actually added 
     lock (dataCacheSizeLockObj) 
     { 
      dataCacheSize -= retVal.Length; 
     } 
    } 
} 
+0

Это не работает. Он по-прежнему будет добавлять retVal.Length дважды в dataCacheSize, когда 2 потока добавили одну и ту же запись одновременно. – Ben

+0

Для устранения этого состояния гонки можно использовать метод 'GetOrAdd' вместо' dataCache [key] = retVal' (я обновил ответ). Но, вероятно, лучше использовать System.Runtime.Caching.MemoryCache. –

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

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