使用unordered_map 和 双向链表实现,支持设置缓存过期时间;
其中使用到的any.h 可查询我另一篇文章 任意类型容器
#ifndef _lru_h_
#define _lru_h_
#include
#include
#include
#include "any.h"
template
class lrucache {
public:
struct node {
key key;
any val;
time_t expire;
node* next;
node* prev;
template
void set(const key& k, const val& v, const int& e) {
key = k;
val = v;
expire = e;
}
void init() {
expire = 0;
next = nullptr;
prev = nullptr;
}
};
explicit lrucache(const size_t& capacity = 100, bool pre_alloc = true): m_cap(capacity){
m_head = nullptr;
m_tail = nullptr;
if(pre_alloc) {
m_available_node.resize(m_cap, nullptr);
node* nodes = (node*)malloc(sizeof(node) * m_cap);
m_allocated.push_back(nodes);
for(int i = 0; i < m_cap; i ) {
m_available_node[i] = &nodes[i];
}
}
}
virtual ~lrucache(){
for(void* mem : m_allocated) {
free(mem);
}
m_allocated.clear();
}
public:
template
void put(key key, val val, int expire = 0) {
auto now = time(null);
auto endtime = now expire;
if(expire == 0) {
endtime = 0;
}
std::lock_guard guard(m_mutex);
if(m_map.count(key)) {
m_map[key]->val = val;
m_map[key]->expire = endtime;
deletenode(m_map[key]);
inserthead(m_map[key]);
return;
}else if(m_map.size() >= cap()) {
node* del = deletenode(m_tail);
m_available_node.push_back(del);
}else if(m_available_node.empty()) {
size_t alloc_size = cap() - m_map.size();
if(alloc_size > allocbatch) {
alloc_size = allocbatch;
}
node* nodes = (node*)malloc(sizeof(node) * alloc_size);
m_allocated.push_back(nodes);
for(size_t i = 0; i < alloc_size; i ) {
m_available_node.push_back(&nodes[i]);
}
}
node* node = m_available_node.back();
m_available_node.pop_back();
node->init();
node->set(key, val, endtime);
m_map[key] = node;
inserthead(node);
// 每次插入,都检查尾部节点是否已经过期, 如果过期则立即删除
checktailexpire(now);
}
template
bool get(key key, val& val) {
std::lock_guard guard(m_mutex);
if(!m_map.count(key)) {
return false;
}
auto now = time(null);
if(m_map[key]->expire != 0 && m_map[key]->expire < now) {
node* del = deletenode(m_map[key]);
m_available_node.push_back(del);
m_map.erase(key);
return false;
}
val = m_map[key]->val.template any_cast();
deletenode(m_map[key]);
inserthead(m_map[key]);
// 每次操作都检查一下尾部节点是否已经过期,过期则删除,通常认为尾节点可能会最先过期
checktailexpire(now);
return true;
}
bool delete(key key) {
std::lock_guard guard(m_mutex);
if(!m_map.count(key)) {
return false;
}
deletenode(m_map[key]);
m_available_node.push_back(m_map[key]);
m_map.erase(key);
return true;
}
const size_t& cap() const {
return m_cap;
}
const size_t& size() const {
std::lock_guard guard(m_mutex);
return m_map.size();
}
private:
node* deletenode(node* node) {
if(node->prev) {
node->prev->next = node->next;
}
if(node->next) {
node->next->prev = node->prev;
}
if(node == m_head) {
m_head = node->next;
}
if(node == m_tail) {
m_tail = node->prev;
}
return node;
}
void inserthead(node* node) {
node->next = m_head;
if(m_head) {
m_head->prev = node;
}
node->prev = nullptr;
m_head = node;
if(m_tail == nullptr) {
m_tail = node;
}
}
void checktailexpire(const time_t& now) {
if(m_tail && m_tail->expire < now) {
node* del = deletenode(m_tail);
m_available_node.push_back(del);
}
}
private:
const size_t m_cap;
std::unordered_map m_map;
struct node* m_head;
struct node* m_tail;
std::mutex m_mutex;
std::vector m_available_node; // 可用的node节点池子,用于优化频繁的创建和释放node节点
std::vector m_allocated; // 被分配的首地址
};
#endif