一个简单的线程安全的LRU_Cache实现

前言

本文首发于我的公众号:码农手札,主要介绍linux下c++开发的知识包括网络编程的知识同时也会介绍一些有趣的算法题,欢迎大家关注,利用碎片时间学习一些编程知识,冰冻三尺非一日之寒,让我们一起加油!
实习过程中遇到一个场景,在多线程下用到一个缓存当前分析的结果的map,当时只是用了一个map来解决这个问题,但是当时就觉得这个办法不是很好,因为分析结果基本上是很快就会被更新掉的,但是如果一个结果被用到,说明其很可能会被继续用到,分析了一下场景,我们就能很明了的想到,应该是用LRU来解决这个问题,LRU是一种被广泛使用的缓存算法,在操作系统以及一些开源的库中都能见到其身影,这里我们也对LRU也做出一个简单的拓展,实现一个模板LRUC_Cache类

实现代码

#include 
#include 
#include 

template  using LISTT = std::list;

template 
using PAIRTL = std::pair::iterator>;

template 
using UMAPTP = std::unordered_map>;

template  class LRU_Cache {
public:
  LRU_Cache(const size_t &capacity) : capacity_(capacity) {}
  Y Get(const T &key) {
    std::lock_guard lck(cache_mutex_);
    auto it = cache_.find(key);
    if (it == cache_.end()) {
      return Y();
    }
    touch(it);
    return it->second.first;
  }
  void Put(const T &key, const Y &value) {
    std::lock_guard lck(cache_mutex_);
    auto it = cache_.find(key);
    if (it != cache_.end()) {
      touch(it);
    } else {
      if (cache_.size() == capacity_) {
        cache_.erase(elements_.back());
        elements_.pop_back();
      }
      elements_.push_front(key);
    }
    cache_[key] = {value, elements_.begin()};
  }

private:
  void touch(typename UMAPTP::iterator it) {
    ///注意这里不能加锁,否则就死锁了,因为这个函数不会被单独调用,总是在有锁的情况下
    ///被调用,所以函数本身不需要再加锁
    T key = it->first;
    elements_.erase(it->second.second);
    elements_.push_front(key);
    it->second.second = elements_.begin();
  }
  size_t capacity_;
  ///由于element_和cache_都是被同时访问,因此一个锁就够了
  std::mutex cache_mutex_;
  LISTT elements_;
  UMAPTP cache_;
};

简单解释

不考虑多线程的情况下(其实考虑也没啥,只有一个锁哈哈),我们使用来一个list来储存所有的key,使用unorderedmap来保存key,然后value是一个pair,pair的首元素是value,次元素是指向key在list中的位置的迭代器,这里list的作用其实只是标示元素的顺序,因为lru算法要求每当一个元素被access,它就应该排到队列的最前端,当lru的缓存已经满了的时候,就应该删除队列中最后面的元素,因为这个元素是least_recently_used(lru),unorderedmap的作用也就很明显了,一方面储存value,一方面储存key在list中对应的迭代器,方便进行删除操作。

至于实现的话,这里有个小技巧,就是抽离出了一个touch函数,这个函数的作用是根据一个确定已经在缓存中的元素的key,访问一次,其目的就是改变元素在队列中的位置,同时unorderedmap中的迭代器也需要更新,本质上就是把元素挪到队列头。

其他的应该都比较直接,反而模板这一块由于自己不太熟,写那个using声明弄了蛮久,说起来还怪不好意思

总结

这个实现虽然简单,但是基本效果还是能达到的,我自己也做了简单的测试,可能不是很全面,但是基本还是能用的,毕竟代码也相对比较简单(笑),

你可能感兴趣的:(一个简单的线程安全的LRU_Cache实现)