Есть ли в LRU реализация IDictionary? - PullRequest
26 голосов
/ 16 апреля 2009

Я хотел бы реализовать простую систему кэш-памяти LRU в памяти, и я думал о решении на основе реализации IDictionary, которое могло бы обрабатывать механизм хеширования LRU. Исходя из Java, у меня есть опыт работы с LinkedHashMap, который прекрасно работает для того, что мне нужно: я не могу найти где-либо подобное решение для .NET.

Кто-нибудь разрабатывал это или у кого-то был подобный опыт?

Ответы [ 9 ]

41 голосов
/ 15 сентября 2010

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

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

namespace LRUCache
{
    public class LRUCache<K,V>
    {
        private int capacity;
        private Dictionary<K, LinkedListNode<LRUCacheItem<K, V>>> cacheMap = new Dictionary<K, LinkedListNode<LRUCacheItem<K, V>>>();
        private LinkedList<LRUCacheItem<K, V>> lruList = new LinkedList<LRUCacheItem<K, V>>();

        public LRUCache(int capacity)
        {
            this.capacity = capacity;
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public V get(K key)
        {
            LinkedListNode<LRUCacheItem<K, V>> node;
            if (cacheMap.TryGetValue(key, out node))
            {
                V value = node.Value.value;
                lruList.Remove(node);
                lruList.AddLast(node);
                return value;
            }
            return default(V);
        }

        [MethodImpl(MethodImplOptions.Synchronized)]
        public void add(K key, V val)
        {
            if (cacheMap.Count >= capacity)
            {
                RemoveFirst();
            }

            LRUCacheItem<K, V> cacheItem = new LRUCacheItem<K, V>(key, val);
            LinkedListNode<LRUCacheItem<K, V>> node = new LinkedListNode<LRUCacheItem<K, V>>(cacheItem);
            lruList.AddLast(node);
            cacheMap.Add(key, node);
        }

        private void RemoveFirst()
        {
            // Remove from LRUPriority
            LinkedListNode<LRUCacheItem<K,V>> node = lruList.First;
            lruList.RemoveFirst();

            // Remove from cache
            cacheMap.Remove(node.Value.key);
        }
    }

    class LRUCacheItem<K,V>
    {
        public LRUCacheItem(K k, V v)
        {
            key = k;
            value = v;
        }
        public K key;
        public V value;
    }
}
7 голосов
/ 16 апреля 2009

В библиотеках базовых классов нет ничего, что могло бы сделать это.

На бесплатной стороне, возможно, что-то вроде HashedLinkedList C5 будет работать.

Если вы готовы платить, возможно, посмотрите этот инструментарий C #. Он содержит реализацию.

5 голосов
/ 03 июня 2011

Нашел ваш ответ во время поиска в Google, также нашел это:

http://code.google.com/p/csharp-lru-cache/

csharp-lru-cache : библиотека классов коллекции LRU-кешей

Это класс коллекции, который функционирует как наименее недавно используемый кэш. Он реализует ICollection<T>, но также выставляет трех других членов:

  • Capacity, максимальное количество предметов кеш может содержать. Однажды сбор в полном объеме, добавив новый элемент в кеш вызовет наименее недавно использованный элемент отбрасываются. Если Емкость установлена ​​на 0 на стройке кеша не будет автоматически сбрасывать предметы.
  • Oldest, самый старый (т.е. использованный в последнее время) предмет в коллекции.
  • DiscardingOldestItem, событие поднято когда кеш собирается сбросить его самый старый предмет Это чрезвычайно простая реализация. Хотя его Добавить и методы Remove являются поточно-ориентированными, это не должен использоваться в тяжелых многопоточность среды, потому что вся коллекция заблокирована во время эти методы.
4 голосов
/ 07 апреля 2017

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

namespace LruCache
{
    using System;
    using System.Collections.Generic;

    /// <summary>
    /// A least-recently-used cache stored like a dictionary.
    /// </summary>
    /// <typeparam name="TKey">
    /// The type of the key to the cached item
    /// </typeparam>
    /// <typeparam name="TValue">
    /// The type of the cached item.
    /// </typeparam>
    /// <remarks>
    /// Derived from https://stackoverflow.com/a/3719378/240845
    /// </remarks>
    public class LruCache<TKey, TValue>
    {
        private readonly Dictionary<TKey, LinkedListNode<LruCacheItem>> cacheMap =
            new Dictionary<TKey, LinkedListNode<LruCacheItem>>();

        private readonly LinkedList<LruCacheItem> lruList =
            new LinkedList<LruCacheItem>();

        private readonly Action<TValue> dispose;

        /// <summary>
        /// Initializes a new instance of the <see cref="LruCache{TKey, TValue}"/>
        /// class.
        /// </summary>
        /// <param name="capacity">
        /// Maximum number of elements to cache.
        /// </param>
        /// <param name="dispose">
        /// When elements cycle out of the cache, disposes them. May be null.
        /// </param>
        public LruCache(int capacity, Action<TValue> dispose = null)
        {
            this.Capacity = capacity;
            this.dispose = dispose;
        }

        /// <summary>
        /// Gets the capacity of the cache.
        /// </summary>
        public int Capacity { get; }

        /// <summary>Gets the value associated with the specified key.</summary>
        /// <param name="key">
        /// The key of the value to get.
        /// </param>
        /// <param name="value">
        /// When this method returns, contains the value associated with the specified
        /// key, if the key is found; otherwise, the default value for the type of the 
        /// <paramref name="value" /> parameter. This parameter is passed
        /// uninitialized.
        /// </param>
        /// <returns>
        /// true if the <see cref="T:System.Collections.Generic.Dictionary`2" /> 
        /// contains an element with the specified key; otherwise, false.
        /// </returns>
        public bool TryGetValue(TKey key, out TValue value)
        {
            lock (this.cacheMap)
            {
                LinkedListNode<LruCacheItem> node;
                if (this.cacheMap.TryGetValue(key, out node))
                {
                    value = node.Value.Value;
                    this.lruList.Remove(node);
                    this.lruList.AddLast(node);
                    return true;
                }

                value = default(TValue);
                return false;
            }
        }

        /// <summary>
        /// Looks for a value for the matching <paramref name="key"/>. If not found, 
        /// calls <paramref name="valueGenerator"/> to retrieve the value and add it to
        /// the cache.
        /// </summary>
        /// <param name="key">
        /// The key of the value to look up.
        /// </param>
        /// <param name="valueGenerator">
        /// Generates a value if one isn't found.
        /// </param>
        /// <returns>
        /// The requested value.
        /// </returns>
        public TValue Get(TKey key, Func<TValue> valueGenerator)
        {
            lock (this.cacheMap)
            {
                LinkedListNode<LruCacheItem> node;
                TValue value;
                if (this.cacheMap.TryGetValue(key, out node))
                {
                    value = node.Value.Value;
                    this.lruList.Remove(node);
                    this.lruList.AddLast(node);
                }
                else
                {
                    value = valueGenerator();
                    if (this.cacheMap.Count >= this.Capacity)
                    {
                        this.RemoveFirst();
                    }

                    LruCacheItem cacheItem = new LruCacheItem(key, value);
                    node = new LinkedListNode<LruCacheItem>(cacheItem);
                    this.lruList.AddLast(node);
                    this.cacheMap.Add(key, node);
                }

                return value;
            }
        }

        /// <summary>
        /// Adds the specified key and value to the dictionary.
        /// </summary>
        /// <param name="key">
        /// The key of the element to add.
        /// </param>
        /// <param name="value">
        /// The value of the element to add. The value can be null for reference types.
        /// </param>
        public void Add(TKey key, TValue value)
        {
            lock (this.cacheMap)
            {
                if (this.cacheMap.Count >= this.Capacity)
                {
                    this.RemoveFirst();
                }

                LruCacheItem cacheItem = new LruCacheItem(key, value);
                LinkedListNode<LruCacheItem> node = 
                    new LinkedListNode<LruCacheItem>(cacheItem);
                this.lruList.AddLast(node);
                this.cacheMap.Add(key, node);
            }
        }

        private void RemoveFirst()
        {
            // Remove from LRUPriority
            LinkedListNode<LruCacheItem> node = this.lruList.First;
            this.lruList.RemoveFirst();

            // Remove from cache
            this.cacheMap.Remove(node.Value.Key);

            // dispose
            this.dispose?.Invoke(node.Value.Value);
        }

        private class LruCacheItem
        {
            public LruCacheItem(TKey k, TValue v)
            {
                this.Key = k;
                this.Value = v;
            }

            public TKey Key { get; }

            public TValue Value { get; }
        }
    }
}
4 голосов
/ 29 января 2014

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

Основные функции:

  • Связанный параллельный словарь по вставке, модификации или доступу
  • Поддержка интерфейса Dictionary / ConcurrentDictionary
  • Peek / TryDequeue / Dequeue доступ к «самой старой» записи
  • Позволяет жестко ограничить элементы, вводимые в действие при вставке
  • Выставляет события для добавления, обновления и удаления

Исходный код: http://csharptest.net/browse/src/Library/Collections/LurchTable.cs

GitHub: https://github.com/csharptest/CSharpTest.Net.Collections

HTML Help: http://help.csharptest.net/

PM> Install-Package CSharpTest.Net.Collections

3 голосов
/ 16 апреля 2009

Блок приложения Caching EntLib имеет опцию очистки LRU и может находиться в памяти. Это может быть немного тяжеловесно для того, что вы хотите.

2 голосов
/ 06 ноября 2010

Мне нравится реализация Лоуренса. Hashtable + LinkedList - хорошее решение. Что касается потоков, я бы не стал блокировать этот [MethodImpl (MethodImplOptions.Synchronized)], а вместо этого использовал бы ReaderWriterLockSlim или спин-блокировку (так как конфликт обычно быстрая). В функции get я бы проверил, если это уже первый элемент, а не всегда удаляю и добавляю. Это дает вам возможность удерживать блокировку чтения, которая не блокирует другие устройства чтения.

2 голосов
/ 16 апреля 2009

Я не верю в это. Я, конечно, видел, как раскатанные вручную реализованы несколько раз в различных несвязанных проектах (что более или менее подтверждает это. Если бы он был, наверняка хотя бы один из проектов использовал бы его).

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

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

0 голосов
/ 16 апреля 2009

Если это приложение asp.net, вы можете использовать класс кеша [1], но вы будете бороться за место с другим кешированным материалом, который может быть тем, что вы хотите, а может и не быть.

[1] http://msdn.microsoft.com/en-us/library/system.web.caching.cache.aspx

...