0

0

c# 如何用 C# 实现一个高性能的内存缓存(LRU, LFU)

星降

星降

发布时间:2026-01-04 11:13:10

|

597人浏览过

|

来源于php中文网

原创

MemoryCache 不支持自定义驱逐逻辑,无法实现精确的 LRU/LFU;需手动用 LinkedList+Dictionary 实现 O(1) 淘汰,注意线程安全与节点定位陷阱。

c# 如何用 c# 实现一个高性能的内存缓存(lru, lfu)

为什么不用 MemoryCache 直接上手写 LRU/LFU?

MemoryCache 是 .NET 内置的线程安全缓存,支持过期策略和内存压力回收,但它不暴露访问频次或访问顺序的底层控制点,也无法定制驱逐逻辑(比如严格按 LFU 计数淘汰,或带权重的 LRU)。当你需要:精确控制淘汰行为、避免 GC 压力(如高频小对象)、做缓存命中率热力分析、或嵌入到低延迟服务中时,就得自己实现。

LinkedList + Dictionary> 实现 O(1) LRU

核心是把“最近使用”映射为链表头,“最久未用”固定在尾。每次 Get 就把对应节点移到头;Set 时若已存在就更新值并移至头,否则新建节点插入头,超容则删尾节点。

关键陷阱:

  • LinkedListNode 持有对 T 的引用,但 Dictionary 的 value 是节点本身 —— 别误存值或键,否则无法 O(1) 定位
  • 多线程下必须加锁,ConcurrentDictionary 不能直接替代 Dictionary,因为你要原子地「查字典 + 移动节点」,得用 lockReaderWriterLockSlim
  • 不要在 LinkedList 上反复调用 Find —— 那是 O(N),彻底毁掉设计初衷
public class LRUCache
{
    private readonly int _capacity;
    private readonly Dictionary> _map;
    private readonly LinkedList<(K, V)> _list;

    public LRUCache(int capacity)
    {
        _capacity = capacity;
        _map = new Dictionary>();
        _list = new LinkedList<(K, V)>();
    }

    public V? Get(K key)
    {
        if (!_map.TryGetValue(key, out var node)) return default;
        _list.Remove(node);         // O(1)
        _list.AddFirst(node);       // O(1)
        return node.Value.Item2;
    }

    public void Put(K key, V value)
    {
        if (_map.TryGetValue(key, out var node))
        {
            node.Value = (key, value);
            _list.Remove(node);
            _list.AddFirst(node);
        }
        else
        {
            var newNode = _list.AddFirst((key, value));
            _map[key] = newNode;

            if (_map.Count > _capacity)
            {
                var last = _list.Last!;
                _list.RemoveLast();
                _map.Remove(last.Value.Item1);
            }
        }
    }
}

LFU 实现难点:如何 O(1) 更新频次并找到最小频次的候选键?

LFU 要求每个键记录访问次数,并在容量满时淘汰「访问次数最少且最久未用」的项。纯靠 Dictionary + 线性扫描找 min freq → O(N),不可接受。

标准解法是双哈希结构:

  • Dictionary node)>:存值、频次、以及它在频次链表中的位置
  • Dictionary>:按频次分组,每个频次对应一个 LRU 链表(用于同频次下淘汰最久未用者)
  • 维护一个全局 minFreq,每次淘汰只看 _freqLists[minFreq].First

每次 Get:从原频次链表删节点 → 加入 freq+1 链表 → 更新 minFreq(如果原链表空了且等于 minFreq,则 minFreq++

腾讯混元文生视频
腾讯混元文生视频

腾讯发布的AI视频生成大模型技术

下载

注意:minFreq 只增不减,且仅在 Put 新键时重置为 1;Get 不会重置 minFreq 为 1。

性能与取舍:什么时候该用 ConcurrentDictionary

如果你的缓存读远多于写(比如 >95% 是 Get),且能接受「短暂不一致」(例如两个线程几乎同时 Get 同一 key,都触发回源加载),那么用 ConcurrentDictionary + 无锁 GetOrAdd 回源更轻量 —— 但这就不是严格 LRU/LFU 了。

真正需要强一致 LRU/LFU 的场景(如分布式 session 本地镜像、风控规则热加载),必须用细粒度锁或 ReaderWriterLockSlim,尤其注意 Put 中「删除尾节点 + 更新字典」必须原子。

另外:.NET 6+ 的 System.Collections.Generic.PriorityQueue 不适合这里 —— 它不支持 O(1) 修改优先级,每次更新频次都要重新入队,退化成 O(log N)。

相关文章

数码产品性能查询
数码产品性能查询

该软件包括了市面上所有手机CPU,手机跑分情况,电脑CPU,电脑产品信息等等,方便需要大家查阅数码产品最新情况,了解产品特性,能够进行对比选择最具性价比的商品。

下载

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

相关专题

更多
什么是分布式
什么是分布式

分布式是一种计算和数据处理的方式,将计算任务或数据分散到多个计算机或节点中进行处理。本专题为大家提供分布式相关的文章、下载、课程内容,供大家免费下载体验。

321

2023.08.11

分布式和微服务的区别
分布式和微服务的区别

分布式和微服务的区别在定义和概念、设计思想、粒度和复杂性、服务边界和自治性、技术栈和部署方式等。本专题为大家提供分布式和微服务相关的文章、下载、课程内容,供大家免费下载体验。

229

2023.10.07

session失效的原因
session失效的原因

session失效的原因有会话超时、会话数量限制、会话完整性检查、服务器重启、浏览器或设备问题等等。详细介绍:1、会话超时:服务器为Session设置了一个默认的超时时间,当用户在一段时间内没有与服务器交互时,Session将自动失效;2、会话数量限制:服务器为每个用户的Session数量设置了一个限制,当用户创建的Session数量超过这个限制时,最新的会覆盖最早的等等。

302

2023.10.17

session失效解决方法
session失效解决方法

session失效通常是由于 session 的生存时间过期或者服务器关闭导致的。其解决办法:1、延长session的生存时间;2、使用持久化存储;3、使用cookie;4、异步更新session;5、使用会话管理中间件。

708

2023.10.18

cookie与session的区别
cookie与session的区别

本专题整合了cookie与session的区别和使用方法等相关内容,阅读专题下面的文章了解更详细的内容。

88

2025.08.19

string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

314

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

524

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

49

2025.08.29

从零到实战:Python 编程系统入门专题
从零到实战:Python 编程系统入门专题

本专题面向零编程基础及初学者,系统讲解 Python 编程语言的核心知识与实战技巧。内容涵盖 Python 基础语法、数据结构、函数与模块、常用标准库、简单算法思维,以及真实应用场景下的小项目实战。通过循序渐进的学习路径,帮助读者快速建立编程思维,掌握 Python 在数据处理、自动化脚本及日常开发中的实际应用能力,为后续深入学习 Web 开发、数据分析或人工智能打下坚实基础。

2

2026.01.05

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
HTML5/CSS3/JavaScript/ES6入门课程
HTML5/CSS3/JavaScript/ES6入门课程

共102课时 | 6.6万人学习

前端基础到实战(HTML5+CSS3+ES6+NPM)
前端基础到实战(HTML5+CSS3+ES6+NPM)

共162课时 | 18.5万人学习

第二十二期_前端开发
第二十二期_前端开发

共119课时 | 12.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号