c# LRU實現的快取類
在網上找到網友中的方法,將其修改整理後,實現了快取量控制以及時間控制,如果開啟快取時間控制,會降低效率。
定義列舉,移除時使用
public enum RemoveType { [Description("超時移除")] TimeOut, [Description("超量移除")] Capacity }
定義委託,移除時使用
public delegate void RemoveKV<Tkey, TValue>(string cacheName, RemoveEntity<Tkey, TValue> entity);
定義快取類
public class LRUCache<TKey, TValue> { const int DEFAULT_CAPACITY = int.MaxValue;
private int _capacity; private ReaderWriterLockSlim locker; private IDictionary<TKey, TValue> dictionary;//資料 private LinkedList<TKey> linkedList;//控制Key的資料 private Dictionary<TKey, LinkedListNode<TKey>> dicLinkIndex = null;//控制刪除 private Dictionary<TKey, long> dicRefresh = null;//使用時間 private volatile bool isCheckTime = false;//設定是否監測快取的時間長度 private long cacheTime = 600;//10分鐘 private volatile bool isCheckThread = false;//檢查執行緒啟動 private long checkTicks = 0;//換算後的時間 private BlockingCollection<RemoveEntity<TKey, TValue>> removeEntities = null; public event RemoveKV<TKey, TValue> RemoveEntitiesEvent = null;//移除通知 private DateTime checkTime = DateTime.Now;//結束監測的時間
/// <summary> /// 設定快取的時間長度,當前按照秒算 /// 設定時間自動設定屬性IsCacheCheckTime=true /// </summary> public long CacheTime { get { return cacheTime; } set { cacheTime = value;isCheckTime = true; countTime(); } }
/// <summary> /// 是否監測時間 /// </summary> public bool IsCacheCheckTime { get { return isCheckTime; } set { isCheckTime = value; countTime(); } }
/// <summary> /// cache名稱 /// </summary> public string CacheName { get; set; }
public LRUCache() : this(DEFAULT_CAPACITY) { }
public LRUCache(int capacity) { locker = new ReaderWriterLockSlim(); _capacity = capacity > 0 ? capacity : DEFAULT_CAPACITY; dictionary = new Dictionary<TKey, TValue>(); linkedList = new LinkedList<TKey>(); dicLinkIndex = new Dictionary<TKey, LinkedListNode<TKey>>(); dicRefresh = new Dictionary<TKey, long>(); removeEntities = new BlockingCollection<RemoveEntity<TKey, TValue>>(1000); countTime(); RemoveNotice(); }
/// <summary> /// 換算時間; /// 將秒轉換成ticks個數 /// </summary> private void countTime() { checkTicks = 10000000 * cacheTime; }
/// <summary> /// 更新時間 /// </summary> /// <param name="key"></param> private void Refresh(TKey key) { dicRefresh[key] = DateTime.Now.Ticks; if(!isCheckTime) { return; } if(!isCheckThread) { isCheckThread = true; Task.Factory.StartNew(() => { double wait = (DateTime.Now - checkTime).TotalSeconds; if(wait<cacheTime) { //如果上次監測到本次監測還未到設定的保持時間, //則等待該時間差後再檢查 double sleep = ((double)cacheTime-wait) * 1000+1; Thread.Sleep((int)sleep); } locker.EnterWriteLock(); try { LinkedListNode<TKey> last = null; long tick; long curTick = DateTime.Now.Ticks; last = linkedList.Last;//重後往前找 while (last != null) { if (dicRefresh.TryGetValue(last.Value, out tick)) { if ((curTick - tick) > checkTicks) { dicLinkIndex.Remove(last.Value); dicRefresh.Remove(last.Value); linkedList.RemoveLast(); RemoveEntity<TKey, TValue> entity = new RemoveEntity<TKey, TValue>() { Key = last.Value, Value = dictionary[last.Value], RemoveType= RemoveType.TimeOut }; removeEntities.Add(entity); dictionary.Remove(last.Value); } else { break; } } last = linkedList.Last; } } finally { locker.ExitWriteLock(); } isCheckThread = false; checkTime = DateTime.Now; }); } }
private void RemoveNotice() { Task.Factory.StartNew(() => { while(true) {
RemoveEntity<TKey, TValue> item=null; if(removeEntities.TryTake(out item,500)) { if(this.RemoveEntitiesEvent != null) { RemoveEntitiesEvent(CacheName, item); } } } }); }
public void Set(TKey key, TValue value) { locker.EnterWriteLock(); try { dictionary[key] = value; LinkedListNode<TKey> item = null; if(dicLinkIndex.TryGetValue(key,out item)) { linkedList.Remove(item); } dicLinkIndex[key]= linkedList.AddFirst(key); if (linkedList.Count > _capacity) { dictionary.Remove(linkedList.Last.Value); dicLinkIndex.Remove(linkedList.Last.Value); linkedList.RemoveLast(); dicRefresh.Remove(linkedList.Last.Value); RemoveEntity<TKey, TValue> entity = new RemoveEntity<TKey, TValue>() { Key = linkedList.Last.Value, Value = dictionary[linkedList.Last.Value], RemoveType = RemoveType.Capacity }; removeEntities.Add(entity); dictionary.Remove(linkedList.Last.Value); } Refresh(key); } finally { locker.ExitWriteLock(); } }
public bool TryGet(TKey key, out TValue value) { locker.EnterUpgradeableReadLock(); try { bool b = dictionary.TryGetValue(key, out value); if (b) { locker.EnterWriteLock(); try { linkedList.Remove(key); linkedList.AddFirst(key); } finally { locker.ExitWriteLock(); }
} Refresh(key); return b; } catch { throw; } finally { locker.ExitUpgradeableReadLock(); } } public void Clear() { locker.EnterWriteLock(); try { dictionary.Clear(); linkedList.Clear(); dicRefresh.Clear(); dicLinkIndex.Clear(); dicRefresh.Clear(); } finally { locker.ExitWriteLock(); } } public bool Remove(TKey key) { bool isSucess = false; locker.EnterWriteLock(); try { isSucess = dictionary.Remove(key); dicRefresh.Remove(key); LinkedListNode<TKey> item = null; if (dicLinkIndex.TryGetValue(key, out item)) { linkedList.Remove(item); } } finally { locker.ExitWriteLock(); } return isSucess; } public bool ContainsKey(TKey key) { locker.EnterReadLock(); try { return dictionary.ContainsKey(key); } finally { locker.ExitReadLock(); } }
public int Count { get { locker.EnterReadLock(); try { return dictionary.Count; } finally { locker.ExitReadLock(); } } }
public int Capacity { get { locker.EnterReadLock(); try { return _capacity; } finally { locker.ExitReadLock(); } } set { locker.EnterUpgradeableReadLock(); try { if (value > 0 && _capacity != value) { locker.EnterWriteLock(); try { _capacity = value; while (linkedList.Count > _capacity) { linkedList.RemoveLast(); } } finally { locker.ExitWriteLock(); } } } finally { locker.ExitUpgradeableReadLock(); } } }
public ICollection<TKey> Keys { get { locker.EnterReadLock(); try { return dictionary.Keys; } finally { locker.ExitReadLock(); } } }
public ICollection<TValue> Values { get { locker.EnterReadLock(); try { return dictionary.Values; } finally { locker.ExitReadLock(); } } } }
//最後在定一個異常的實體
public class RemoveEntity<TKey,TValue> { public TKey Key { get; set; } public TValue Value { get; set; }
public RemoveType RemoveType { get; set; } }
就完成了,測試程式碼
LRUCache<int, int> cache = new LRUCache<int, int>(); cache.RemoveEntitiesEvent += Cache_RemoveEntitiesEvent; cache.CacheTime =3;//啟動快取時間 Random random = new Random(); DateTime start= DateTime.Now; for(int i=0;i<10000000;i++) { cache.Set(i,random.Next()); } Console.WriteLine("時間:" + (DateTime.Now - start).TotalSeconds); Console.Read();
總共10-12秒;如果遮蔽快取時間設定,7秒
程式碼已經傳到GIT,地址和上一篇快取模板一直,整合在同一個專案中。