跳转至

3. LruCache

使用下述功能需要包含头文件coke/lru_cache.h

coke::LruCache

LruCache是一个基于LRU淘汰策略的并发安全的缓存容器,支持异步等待缓存就绪。适用于需要缓存热点数据且需要协调多个协程访问同一资源的场景。

template<typename K, typename V, typename H = std::hash<K>,
         typename E = std::equal_to<>>
class LruCache;

LruCache使用哈希表管理数据,默认使用std::hash<K>计算key的哈希值,使用std::equal_to<void>比较两个key是否等价。

成员类型

using Key = K;
using Value = V;
using Hash = H;
using Equal = E;
using Entry = detail::LruEntry<K, V>;
using Handle = detail::LruHandle<K, V>;
using SizeType = std::size_t;

成员函数

  • 构造函数

    创建指定容量的LruCache。若max_size为0则转换为SizeType(-1)

    explicit LruCache(SizeType max_size = 0, const Hash &hash = Hash(),
                      const Equal &equal = Equal());
    

    LruCache不可移动、不可复制。

    LruCache(LruCache &&) = delete;
    LruCache &operator=(LruCache &&) = delete;
    
    LruCache(const LruCache &) = delete;
    LruCache &operator=(const LruCache &) = delete;
    
  • 析构函数

    销毁LruCache,应保证所有通过get_or_create返回的handle都不再处于等待状态。

    ~LruCache();
    
  • 容量查询

    // 获取当前缓存的条目数
    SizeType size() const;
    
    // 获取构造时传入的缓存容量
    SizeType capacity() const;
    
  • 获取条目

    通过键查找条目,返回空handle表示未命中。命中时会自动将key升热。

    template<typename U>
        requires HashEqualComparable<Hash, Equal, Key, U>
    Handle get(const U &key);
    
  • 获取或创建条目

    返回(handle, created)对。当createdtrue时,调用者应负责填充值并唤醒等待者。

    template<typename U>
        requires (HashEqualComparable<Hash, Equal, Key, U> &&
                  std::constructible_from<Key, const U &>)
    [[nodiscard]]
    std::pair<Handle, bool> get_or_create(const U &key);
    
  • 插入条目

    直接插入条目,使用args...构造value,并返回对应handle。若key存在则会被覆盖,此前已经获取的与旧值关联的handle仍能访问旧的值。

    template<typename U, typename... Args>
        requires (HashEqualComparable<Hash, Equal, Key, const U &> &&
                  std::constructible_from<Key, const U &> &&
                  std::constructible_from<Value, Args && ...>)
    Handle put(const U &key, Args &&...args);
    
  • 删除条目

    支持通过键或handle删除。条目会立即被移除,但此前已经获取的handle仍可使用。

    template<typename U>
        requires HashEqualComparable<Hash, Equal, Key, const U &>
    void remove(const U &key);
    
    void remove(const Handle &handle);
    
  • 清空缓存

    void clear();
    

coke::detail::LruHandle

  • 构造、赋值、析构函数

    // 创建一个空LruHandle,不关联任何条目。
    LruHandle() noexcept;
    
    // 复制构造,创建一个新的LruHandle,关联同一个条目。
    LruHandle(const LruHandle &) noexcept;
    
    // 移动构造
    LruHandle(LruHandle &&) noexcept;
    
    // 复制赋值,关联同一个条目。
    LruHandle &operator=(const LruHandle &) noexcept;
    
    // 移动赋值
    LruHandle &operator=(LruHandle &&) noexcept;
    
    // 析构函数,自动释放关联的条目。
    ~LruHandle();
    
  • 状态检查

    // 是否关联条目
    operator bool() const noexcept;
    
    // 是否正在等待值
    bool waiting() const noexcept;
    
    // 是否成功设置值
    bool success() const noexcept;
    
    // 是否标记为失败
    bool failed() const noexcept;
    
  • 值操作

    填充值或标记失败状态后,均需要调用notify_one/notify_all唤醒等待者。

    // 原地构造值
    template<typename... Args>
    void emplace_value(Args &&...args)
    
    // 通过回调函数设置值
    template<typename ValueCreater>
    void create_value(ValueCreater &&creater)
    
    // 标记为失败状态
    void set_failed();
    
  • 异步等待

    协程返回一个int类型整数,有以下几种情况

    • coke::TOP_SUCCESS表示成功取出
    • coke::TOP_TIMEOUT表示等待超时,wait_for可能返回该值
    • coke::TOP_ABORTED表示进程正在退出
    • coke::TOP_CLOSED表示容器为空且已被关闭
    • 负数表示发生系统错误,该情况几乎不会发生
    // 等待至状态变化
    Task<int> wait();
    
    // 带超时的等待
    Task<int> wait_for(coke::NanoSec nsec);
    
  • 唤醒机制

    // 唤醒单个等待协程
    void notify_one();
    
    // 唤醒所有等待协程
    void notify_all();
    
  • 获取值

    // 获取key
    const Key &key() const noexcept;
    
    // 获取value,要求此时处于成功状态
    const Value &value() const noexcept;
    
    // 获取可修改的value,要求此时处于成功状态。使用者保证后续对value的修改是线程安全的。
    Value &mutable_value() noexcept;
    

示例

参考example/ex018-lru_cache.cpp