/// <summary> /// 清空缓存中的所有对象。 /// </summary> private void ClearInternal() { LruNode <TKey, TValue> oldHead; cacheLock.EnterWriteLock(); try { cacheDict.Clear(); oldHead = head; head = codeHead = null; count = 0; } finally { cacheLock.ExitWriteLock(); } if (IsDisposable) { // 释放对象资源。 LruNode <TKey, TValue> node = oldHead; do { IDisposable disposable = node.Value as IDisposable; if (disposable != null) { disposable.Dispose(); } node = node.Next; } while (node != oldHead); } }
/// <summary> /// 向当前节点之前添加新节点。 /// </summary> /// <param name="node">要添加的新节点。</param> public void AddBefore(LruNode <TKey, TValue> node) { node.Next = this; node.Prev = this.Prev; this.Prev.Next = node; this.Prev = node; }
/// <summary> /// 向当前节点之前添加新节点。 /// </summary> /// <param name="node">要添加的新节点。</param> public void AddBefore(LruNode <TKey, TValue> node) { Contract.Requires(node != null); node.Next = this; node.Prev = this.Prev; this.Prev.Next = node; this.Prev = node; }
/// <summary> /// 将指定的节点添加到链表冷端的头部,链表操作本身是非线程安全的。 /// </summary> /// <param name="node">要添加的节点。</param> private void AddCodeFirst(LruNode <TKey, TValue> node) { Contract.Requires(node != null); Contract.Assume(this.codeHead != null); // 这里 codeHead != null,在调用的时候已经保证了这一点。 this.codeHead.AddBefore(node); this.codeHead = node; }
/// <summary> /// 清空缓存中的所有对象。 /// </summary> public void Clear() { lock (cacheLock) { cacheDict.Clear(); head = codeHead = null; count = 0; } }
/// <summary> /// 将指定的键和对象添加到缓存中,并返回添加的节点。 /// </summary> /// <param name="key">要添加的对象的键。</param> /// <param name="value">要添加的对象。</param> /// <param name="force">是否要强制更新已有键相应的对象。</param> private LruNode <TKey, TValue> AddInternal(TKey key, Lazy <TValue> value, bool force) { Contract.Requires(key != null && value != null); LruNode <TKey, TValue> newNode = new LruNode <TKey, TValue>(key, value); LruNode <TKey, TValue> node = cacheDict.AddOrUpdate(key, newNode, (k, v) => { if (force) { v.Value = value; } return(v); }); if (node != newNode) { return(node); } // 将节点添加到链表中。 lock (cacheLock) { if (node.VisitCount != -1) { if (count < maxSize) { // 将新节点添加到热端的头。 AddHotFirst(newNode); count++; if (count == hotSize + 1) { codeHead = head.Prev; } } else { // 从冷端末尾尝试淘汰旧节点,将访问次数大于 1 的移动到热端的头。 // 由于双向链表是环形存储的,就相当于将 head 前移。 while (head.Prev.VisitCount >= 2) { // 清零访问计数。 head.Prev.VisitCount = 0; head = head.Prev; codeHead = codeHead.Prev; } // 将旧 node 移除。 if (!comparer.Equals(head.Prev.Key, key)) { this.cacheDict.TryRemove(head.Prev.Key, out node); } this.Remove(head.Prev); // 将新节点添加到冷端的头。 this.AddCodeFirst(newNode); } } return(newNode); } }
/// <summary> /// 将指定的节点添加到链表热端的头部。 /// </summary> /// <param name="node">要添加的节点。</param> private void AddHotFirst(LruNode <TKey, TValue> node) { if (this.head == null) { node.Next = node.Prev = node; } else { this.head.AddBefore(node); // 热端长度增加,将冷端头节点像前移动一个位置。 if (this.codeHead != null) { this.codeHead = this.codeHead.Prev; } } this.head = node; }
/// <summary> /// 将指定的节点添加到链表热端的头部,链表操作本身是非线程安全的。 /// </summary> /// <param name="node">要添加的节点。</param> private void AddHotFirst(LruNode <TKey, TValue> node) { Contract.Requires(node != null); if (head == null) { node.Next = node.Prev = node; } else { head.AddBefore(node); // 热端长度增加,将冷端头节点像前移动一个位置。 if (codeHead != null) { codeHead = codeHead.Prev; } } head = node; }
/// <summary> /// 从链表中移除指定的节点。 /// </summary> /// <param name="node">要移除的节点。</param> private void Remove(LruNode <TKey, TValue> node) { if (node.Next == node) { this.head = null; } else { node.Next.Prev = node.Prev; node.Prev.Next = node.Next; if (this.head == node) { this.head = node.Next; } else if (this.codeHead == node) { this.codeHead = node.Next; } } }
/// <summary> /// 从链表中移除指定的节点,链表操作本身是非线程安全的。 /// </summary> /// <param name="node">要移除的节点。</param> private void Remove(LruNode <TKey, TValue> node) { Contract.Requires(node != null); if (node.Next == node) { this.head = null; } else { node.Next.Prev = node.Prev; node.Prev.Next = node.Next; if (this.head == node) { this.head = node.Next; } else if (this.codeHead == node) { this.codeHead = node.Next; } } }
/// <summary> /// 将指定的键和对象添加到缓存中,并返回添加的节点。 /// </summary> /// <param name="key">要添加的对象的键。</param> /// <param name="value">要添加的对象。</param> private void AddInternal(TKey key, TValue value) { LruNode <TKey, TValue> node; IDisposable disposable = null; cacheLock.EnterWriteLock(); try { if (cacheDict.TryGetValue(key, out node)) { // 更新节点。 node.Value = value; // 写锁互斥,这里不用 Interlocked。 node.VisitCount++; return; } else { if (count < maxSize) { // 将节点添加到热端起始。 node = new LruNode <TKey, TValue>(key, value); AddHotFirst(node); // 写锁互斥,这里不用 Interlocked。 count++; if (count == hotSize + 1) { codeHead = head.Prev; } cacheDict.Add(key, node); return; } else { // 从冷端末尾尝试淘汰旧节点,将访问次数大于 1 的移动到热端的头。 // 由于双向链表是环形存储的,就相当于将 head 前移。 while (head.Prev.VisitCount >= 2) { // 清零访问计数。 head.Prev.VisitCount = 0; head = head.Prev; codeHead = codeHead.Prev; } // 将 node 移除,并添加到冷端的头。 node = head.Prev; disposable = node.Value as IDisposable; this.cacheDict.Remove(node.Key); this.Remove(node); // 这里直接重用旧节点。 node.Key = key; node.Value = value; node.VisitCount = 1; this.AddCodeFirst(node); cacheDict.Add(key, node); } } } finally { cacheLock.ExitWriteLock(); } if (disposable != null) { disposable.Dispose(); } }
/// <summary> /// 将指定的节点添加到链表冷端的头部。 /// </summary> /// <param name="node">要添加的节点。</param> private void AddCodeFirst(LruNode <TKey, TValue> node) { // 这里 codeHead != null,在调用的时候已经保证了这一点。 this.codeHead.AddBefore(node); this.codeHead = node; }
/// <summary> /// 将指定的节点添加到链表冷端的头部,链表操作本身是非线程安全的。 /// </summary> /// <param name="node">要添加的节点。</param> private void AddCodeFirst(LruNode <TKey, TValue> node) { Debug.Assert(codeHead != null); this.codeHead.AddBefore(node); this.codeHead = node; }