Ответ 1
В библиотеках базового класса ничего не происходит.
На свободной стороне может работать что-то вроде C5 HashedLinkedList.
Если вы готовы заплатить, возможно, зайдите в этот инструментарий С#. Он содержит реализацию.
Я хотел бы реализовать простую систему кэширования LRU в памяти, и я думал о решении на основе реализации IDictionary, которое могло бы обрабатывать хешированный механизм LRU.
Исходя из java, у меня есть опыт работы с LinkedHashMap
, который отлично работает для того, что мне нужно: я не могу найти нигде подобное решение для .NET.
Кто-нибудь его разработал, или у кого-нибудь были такие события?
В библиотеках базового класса ничего не происходит.
На свободной стороне может работать что-то вроде C5 HashedLinkedList.
Если вы готовы заплатить, возможно, зайдите в этот инструментарий С#. Он содержит реализацию.
Это очень простая быстрая реализация, которую мы разработали для нашего веб-сайта.
Мы стараемся как можно больше улучшить код, но сохраняем его в потоковом режиме. Я думаю, что код очень прост и понятен, но если вам нужно какое-то объяснение или руководство, связанное с его использованием, не стесняйтесь спрашивать.
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;
}
}
Нашел ваш ответ во время поиска в Google, также нашел это:
http://code.google.com/p/csharp-lru-cache/
csharp-lru-cache: библиотека классов кэш-памяти LRU
Это класс коллекции, который функционирует как наименее недавно использованный кэш. Он реализует
ICollection<T>
, но также выставляет трех других участников:
Capacity
, максимальное количество предметов кеш может содержать. Однажды сбор в полном объеме, добавив новый элемент в кеш вызовет наименее недавно использованный элемент отбрасываются. Если Емкость установлена на 0 на стройке кеша не будет автоматически сбрасывать предметы.Oldest
, самый старый (т.е. использованный в последнее время) предмет в коллекции.DiscardingOldestItem
, событие поднято когда кеш собирается сбросить его самый старый предмет Это чрезвычайно простая реализация. Хотя его Добавить и методы Remove являются поточно-ориентированными, это не должен использоваться в тяжелых многопоточность среды, потому что вся коллекция заблокирована во время эти методы.
Недавно я выпустил класс под названием LurchTable, чтобы ответить на необходимость варианта С# для LinkedHashMap. Краткое описание LurchTable можно найти здесь.
Основные функции:
Исходный код: http://csharptest.net/browse/src/Library/Collections/LurchTable.cs
GitHub: https://github.com/csharptest/CSharpTest.Net.Collections
Справка HTML: http://help.csharptest.net/
PM > Install-Package CSharpTest.Net.Collections
Это берет Мартин с помощью 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; }
}
}
}
Кэширующий блок приложений EntLib имеет возможность удаления LRU из коробки и может быть в памяти. Это может быть немного тяжеловесом для того, что вы хотите.
Я так не верю. Я определенно видел, что ручные ролики выполнялись несколько раз в разных несвязанных проектах (что более или менее подтверждает это. Если бы это было один, наверняка хотя бы один из проектов использовал бы его).
Это довольно просто реализовать, и обычно это делается путем создания класса, который содержит как Dictionary
, так и List
.
Ключи идут в списке (в порядке), а предметы идут в словаре.
Когда вы добавляете новый элемент в коллекцию, функция проверяет длину списка, вытягивает последний ключ (если он слишком длинный), а затем выдает ключ и значение из словаря для соответствия. Не намного больше этого действительно
Мне нравится реализация Лоуренса. Hashtable + LinkedList - хорошее решение. Что касается потоковой передачи, я бы не заблокировал этот метод [MethodImpl (MethodImplOptions.Synchronized)], а использовал WriteWriterLockSlim или функцию блокировки спина (так как утверждение обычно было быстрым). В функции get я бы проверил, если он уже первый элемент первым, а не всегда удаляет и добавляет. Это дает вам возможность удерживать блокировку считывателя, не блокируя других считывателей.
Если это приложение asp.net, вы можете использовать класс кэша [1], но вы будете конкурировать за место с другими кэшированными материалами, которые могут быть такими, какие вы хотите или не можете быть.
[1] http://msdn.microsoft.com/en-us/library/system.web.caching.cache.aspx
Я создал реализацию кеша LRU
https://github.com/mohsenShakiba/LRUCache