代码之家  ›  专栏  ›  技术社区  ›  Andy

如何改进对缓存的多线程访问(自定义实现)

  •  0
  • Andy  · 技术社区  · 16 年前

    我有一个自定义的缓存实现,它允许缓存 TCacheable<TKey> 使用LRU(最近使用的最少)缓存替换算法的后代。

    每次访问元素时,都会使用以下同步功能将其冒泡到LRU队列的顶部:

    // a single instance is created to handle all TCacheable<T> elements
    public class Cache()
    {
        private TCacheable<T> oldest, newest;
    
        private object syncQueue = new object();
        private void topQueue(TCacheable<T> el)
        {
            lock (syncQueue)
            if (newest != el)
            {
                if (el.elder != null) el.elder.newer = el.newer;
                if (el.newer != null) el.newer.elder = el.elder;
    
                if (oldest == el) oldest = el.newer;
                if (oldest == null) oldest = el;
    
                if (newest != null) newest.newer = el;
                el.newer = null;
                el.elder = newest;
                newest = el;
            }
        }
    }
    

    这个函数的瓶颈是 lock() 运算符,它一次仅将缓存访问限制为一个线程。

    问题 :有没有可能摆脱 lock(syncQueue) 在这个函数中,同时仍然保持队列完整性?

    2 回复  |  直到 16 年前
        1
  •  1
  •   Ben Manes    16 年前

    并发LRU缓存的诀窍不在于移除锁(这是一个兔子洞),而在于分散获取锁的必要性。在读取期间,队列最终可以与表保持一致,但在写入之后应该保持一致,以便正确地选择要逐出的受害者。因此,可以缓冲重新排序并避免锁争用。我在Java实现中证明了这种方法的有效性: http://code.google.com/p/concurrentlinkedhashmap/

    因此,虽然我可以为您的问题提供“是”的答案,但更好的答案是,您不需要移除锁,而是了解它的实际需要时间。

        2
  •  0
  •   anthony    16 年前

    最简单的第一步是在if(最新的)中移动lock语句。=el)阻塞,因为如果newst==el,则根本不需要等待锁。

    除此之外,你还要做一些有条件的作业。您是否真的有让您相信本节在性能方面存在问题的分析信息?

    如果你真的确信优化这门课是最好的利用你的时间, here's a PDF of the paper "Simple, Fast, and Practical Non-Blocking and Blocking Concurrent Queue Algorithms" . Here's an excellent blog post that might be a little bit more straightforward .