Lru cache算法以及他的锁的问题

Lru cache codes:

1. use double linked list for LRU.

2. For the lock, Do we need to put Get operation in lock or not?

    For example:  2.1.  this.cache.TryGetValue(key, out entry); in method GetItem(...);

            2.2. this.cache.ContainsKey(key) in method AddItem(...).

  For 2.1. We have to put it witin lock. because in method TryGetValue(key, out entry), it is not thread-safe.

           when one thread is get the entry index; then another thread delete the entry from entries, it is possible that entries[i] will return another entry value.

  For 2.2. this.cache.ContainsKey(key), which is still not thread-safe. I put it out of lock{}, maybe there is some risk.(it is safe if we put it in lock{}).


// It is from https://referencesource.microsoft.com/#mscorlib/system/collections/generic/dictionary.cs  


using System;
using System.Collections.Generic;
using System.Text;

namespace LruCache_20170421
{
    public class LruCache<T>
    {
        public LruCache(int maxEntries)
        {
            if (maxEntries < 0)
            {
                throw new ArgumentOutOfRangeException("maxEntries", maxEntries, "maxEntries must be greater than or equal zero.");
            }
            this.maxEntries = maxEntries;
        }

        public bool LruCacheEnabled
        {
            get
            {
                return this.maxEntries > 0;
            }
        }

        public void AddItem(string key, T item)
        {
            if (!LruCacheEnabled)
            {
                throw new ApplicationException("LruCache is disabled.");
            }

            if (string.IsNullOrEmpty(key))
            {
                throw new ArgumentNullException("key", "Key cannot be null or empty.");
            }

            if (this.cache.ContainsKey(key))
            {
                return;
            }

            lock (this.cache)
            {
                if (!this.cache.ContainsKey(key))
                {
                    if (this.cache.Count >= maxEntries)
                    {
                        RemoveOldestEntry();
                    }

                    CacheEntry cacheEntry = new CacheEntry();
                    cacheEntry.Key = key;
                    cacheEntry.Item = item;

                    if (listEnd == null)
                    {
                        // set the first element in the list
                        cacheEntry.Previous = null;
                        cacheEntry.Next = null;

                        listHead = cacheEntry;
                        listEnd = cacheEntry;
                    }
                    else
                    {
                        AddElementToEnd(cacheEntry);
                    }

                    this.cache.Add(key, cacheEntry);
                }
            }
        }

        /// <summary>
        /// add the element to the end of the list
        /// </summary>
        /// <param name="cacheEntry"></param>
        private void AddElementToEnd(CacheEntry cacheEntry)
        {
            cacheEntry.Previous = listEnd;
            cacheEntry.Next = null;
            listEnd.Next = cacheEntry;
            listEnd = cacheEntry;
        }

        public T GetItem(string key, out bool cached)
        {
            if (!LruCacheEnabled)
            {
                throw new ApplicationException("LruCache is disabled.");
            }

            if (string.IsNullOrEmpty(key))
            {
                //Logger.LogInformation("Key cannot be null or empty.");
                cached = false;
                return default(T);
            }

            T item = default(T);
            CacheEntry entry;
            lock (this.cache)
            {
                cached = this.cache.TryGetValue(key, out entry);
                if (cached)
                {
                    item = entry.Item;

                    #region move the element to the end of the list
                    if (entry == listEnd)
                    {
                        return listEnd.Item;
                    }

                    if (entry == listHead)
                    {
                        // it is the head of the list

                        // remove from the head
                        listHead = entry.Next;
                        listHead.Previous = null;
                    }
                    else // entry.Previous!=null
                    {
                        // remove from the list
                        entry.Previous.Next = entry.Next;
                        entry.Next.Previous = entry.Previous;
                    }

                    // add to the end
                    AddElementToEnd(entry);

                    #endregion
                }
            }

            return item;
        }

        #region Private

        /// <summary>
        /// Remove the end entry, which is oldest.
        /// </summary>
        private void RemoveOldestEntry()
        {
            if (listHead == null) // cache is null.
            {
                return;
            }

            // remove the element in list head
            CacheEntry entryToRemove = listHead;
            string keyToRemove = entryToRemove.Key;

            if (entryToRemove.Next != null)
            {
                entryToRemove.Next.Previous = null;
            }

            listHead = entryToRemove.Next;
            if (listEnd == entryToRemove)
            {
                listEnd = listHead;
            }

            this.cache.Remove(keyToRemove);
        }

        private readonly Dictionary<string, CacheEntry> cache = new Dictionary<string, CacheEntry>();
        private readonly int maxEntries;
        private CacheEntry listHead; // element with the farest access time
        private CacheEntry listEnd; // element with the nearest access time

        private class CacheEntry
        {
            public string Key;
            public T Item;

            // double linked list
            public CacheEntry Previous; // point the element with the far access time
            public CacheEntry Next; // point the element with the near access time
        }

        #endregion

    }

}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值