C# 实现 key-value 结构自定义缓存 CustomCache

news2024/11/26 11:45:48

功能需求

使用 C# 编写一个 key-value 结构进程内缓存,实现数据的缓存操作,此处所用到的知识点如下:

  • 线程安全的字典 ConcurrentDictionary
  • 设计模式之单例模式(Singleton);
  • 缓存数据【主动 & 被动】过期模式;

cache

key-value 缓存实现

说明:此处基于 .net 6 平台创建控制台项目。

  • 新建 ConsoleApp 项目,添加 CustomCacheHelper.cs 类;
  1. 导入命名空间(namespace
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
  1. CustomCacheHelper.cs 类中编写实现方法
/// <summary>
/// 自定义内存缓存助手
/// </summary>
public sealed class CustomCacheHelper
{
    #region 单例模式
    //创建私有化静态obj锁  
    private static readonly object _ObjLock = new();
    //创建私有静态字段,接收类的实例化对象  
    private static volatile CustomCacheHelper? _Instance = null;
    //构造函数私有化  
    private CustomCacheHelper() { }
    //创建单利对象资源并返回  
    public static CustomCacheHelper GetSingleObj()
    {
        if (_Instance == null)
        {
            lock (_ObjLock)
            {
                if (_Instance == null)
                {
                    _Instance = new CustomCacheHelper();
                }
            }
        }
        return _Instance;
    }
    #endregion

    /// <summary>
    /// 缓存字典 => 【key|value|time】
    /// </summary>
    private static volatile ConcurrentDictionary<string, KeyValuePair<object, DateTime?>> _CacheDictionary = new();

    /// <summary>
    /// 1.主动过期
    /// </summary>
    static CustomCacheHelper()
    {
        Task.Run(() => {
            while (true)
            {
                int millisecondsTimeout = 1000 * 60 * 10;
                Thread.Sleep(millisecondsTimeout); //10分钟检查一次

                if (_CacheDictionary != null && _CacheDictionary.Keys.Count > 0)
                {
                    ICollection<string> listKey = _CacheDictionary.Keys;
                    foreach (var key in listKey)
                    {
                        var valueTime = _CacheDictionary[key];
                        if (valueTime.Value < DateTime.Now)
                        {
                            _CacheDictionary.TryRemove(key, out KeyValuePair<object, DateTime?> value);
                        }
                    }
                }
            }
        });
    }

    /// <summary>
    /// 索引器
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public object this[string key]
    {
        get => _CacheDictionary[key];
        set => _CacheDictionary[key] = new KeyValuePair<object, DateTime?>(value, null);
    }

    /// <summary>
    /// 设置相对过期缓存
    /// </summary>
    /// <param name="key">键</param>
    /// <param name="data">数据包</param>
    /// <param name="seconds">相对过期时间</param>
    public void Set(string key, object data, int seconds)
    {
        var expirationTime = DateTime.Now.AddSeconds(seconds);
        _CacheDictionary[key] = new KeyValuePair<object, DateTime?>(data, expirationTime);
    }

    /// <summary>
    /// 设置绝对过期缓存
    /// </summary>
    /// <param name="key">键<</param>
    /// <param name="data">数据包</param>
    public void Set(string key, object data)
    {
        this[key] = data; // 下面代码等效
        // _CacheDictionary[key] = new KeyValuePair<object, DateTime?>(data, null);
    }

    /// <summary>
    /// 通过key获取缓存value
    /// 2.被动过期
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="key"></param>
    /// <returns></returns>
    public T? Get<T>(string key)
    {
        if (Exist(key))
        {
            //var valueTime = _CacheDictionary[key];
            //return (T)valueTime.Key; //return (T)this[key];

            bool hasValue = _CacheDictionary.TryGetValue(key, out KeyValuePair<object, DateTime?> value);
            if (hasValue)
            {
                return (T)value.Key; //return (T)this[key];
            }
        }

        return default;
    }

    /// <summary>
    /// 获取所有的key
    /// </summary>
    /// <returns></returns>
    public ICollection<string> GetKeys() => _CacheDictionary.Keys;

    /// <summary>
    /// 获取缓存个数
    /// </summary>
    /// <returns></returns>
    public int Count()
    {
        int count = 0;
        if (_CacheDictionary != null)
        {
            count = _CacheDictionary.Count;
        }
        return count;
    }

    /// <summary>
    /// 删除指定key的value
    /// </summary>
    /// <param name="key"></param>
    public void Remove(string key)
    {
        if (Exist(key))
        {
            _CacheDictionary.TryRemove(key, out KeyValuePair<object, DateTime?> value);
        }
    }

    /// <summary>
    /// 清空所有缓存
    /// </summary>
    public void Cleaner()
    {
        if (_CacheDictionary != null && _CacheDictionary.Count > 0)
        {
            foreach (var key in _CacheDictionary.Keys)
            {
                _CacheDictionary.TryRemove(key, out KeyValuePair<object, DateTime?> value);
            }
        }
    }

    /// <summary>
    /// 2.被动过期,保证任何过期缓存都无法取值
    /// 检查key是否存在
    /// </summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public bool Exist(string key)
    {
        bool isExist = false;
        if (!string.IsNullOrWhiteSpace(key) && _CacheDictionary.ContainsKey(key))
        {
            var valTime = _CacheDictionary[key];

            if (valTime.Value != null && valTime.Value > DateTime.Now)
            {
                isExist = true; //缓存没过期
            }
            else
            {
                if (valTime.Value == null)
                {
                    isExist = true; //永久缓存
                }
                else
                {
                    _CacheDictionary.TryRemove(key, out KeyValuePair<object, DateTime?> value); //缓存过期清理
                }
            }
        }
        return isExist;
    }
}

Main 方法使用缓存

由于该项目是采用控制台程序编写,我们可直接在 Main 方法中,添加如下代码:

var customCache = CustomCacheHelper.GetSingleObj();
customCache.Set("key1", "value1");
customCache.Set("key2", "value2", 3);
customCache.Set("key3", "value3", 6);
var keys = customCache.GetKeys();

Console.WriteLine("首次打印:");
foreach (var key in keys)
{
    Console.WriteLine($"time:{DateTime.Now},key={key},value={customCache.Get<string>(key)}");
}

Console.WriteLine("睡眠5s后再次打印:");
Thread.Sleep(5000);
foreach (var key in keys)
{
    Console.WriteLine($"time:{DateTime.Now},key={key},value={customCache.Get<string>(key)}");
}

此处代码中我们添加了三组 key-vaule 数据,其中一个是没有设置过期时间,另外两个设置过期时间,保存数据后,分别打印缓存中保存的数据,再第二次缓存打印前,先让线程睡眠等待 5 秒(5000毫秒),注意观察控制台输出的信息。

ConsoleApp 启动测试

从控制台输出的信息中,我们可以看到 key=key2value 值为空,说明我们内部调用 Exist 方法生效了,key2value 缓存有效时间是 3 秒,第二次打印输出信息时,此时已经睡眠 5 秒,相对于 key2 存储的内存数据已经超时,而 key3value 存储的有效时间是 6 秒,没有超时,所以能个获取到对应的内存数据。

启动测试

通过上面的 demo 演示,我们就实现了一个自定义的进程内缓存助手,在项目中可以很方便的导入使用。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/403065.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

3.10多线程

一.常见锁策略1.悲观锁 vs乐观锁体现在处理锁冲突的态度①悲观锁:预期锁冲突的概率高所以做的工作更多,付出的成本更多,更低效②乐观锁:预期锁冲突的概率低所以做的工作少,付出的成本更低,更搞笑2.读写锁 vs 普通的互斥锁①普通的互斥锁,只有两个操作 加锁和解锁只有两个线程针…

HT32合泰单片机开发环境搭建和配置教程

HT32合泰(Holtek)单片机开发环境搭建安装教程 前言 最近在准备合泰杯的比赛&#xff0c;在看合泰官方的PPT和数据手册学习&#xff0c;顺便做个合泰单片机的开发环境搭建教程。 合泰杯比赛发放的开发板是ESK32-30501&#xff0c;用的单片机是HT32F52352。 合泰杯官网地址&a…

【C++】vector的使用及其模拟实现

这里写目录标题一、vector的介绍及使用1. vector的介绍2. 构造函数3. 遍历方式4. 容量操作及空间增长问题5. 增删查改6. vector二维数组二、vector的模拟实现1. 构造函数2. 迭代器和基本接口3. reserve和resize4. push_back和pop_back5. insert和erase5. 迭代器失效问题5. 浅拷…

Java中的 this 和 super

1 this 关键字 1.1 this 访问本类属性 this 代表对当前对象的一个引用 所谓当前对象&#xff0c;指的是调用当前类中方法或属性的那个对象this只能在方法内部使用&#xff0c;表示对“调用方法的那个对象”的引用this.属性名&#xff0c;表示本对象自己的属性 当对象的属性和…

IntelliJ IDEA 编码设置

1.场景 适用于配置idea文件编码 2.配置 对已经存在的项目设置文件编码 可以设置全局的编码 以及 项目的编码 一般没啥特殊要求 都建议设置为 UTF-8 以及 配置项目的目录的单独编码 也建议UTF-8 idea可以单独设置properties的编码 也建议改为 UTF-8&#xff0c;其中有一个重点…

HCIP --- GRE和MGRE

VPN----虚拟私有网络&#xff1a;依靠ISP或者其他网络管理机构在公有网络基础上构建的专用的安全数据通信网络&#xff0c;只不过该网络是逻辑上的而非物理的。 虚拟&#xff1a;用户不再需要拥有实际的长途数据线路&#xff0c;而是使用公共网络资源建立的属于自己的私有网络…

[论文笔记]Transformer-XL: Attentive Language Models Beyond a Fixed-Length Context

引言 我们知道Transformer很好用&#xff0c;但它设定的最长长度是512。像一篇文章超过512个token是很容易的&#xff0c;那么我们在处理这种长文本的情况下也想利用Transformer的强大表达能力需要怎么做呢&#xff1f; 本文就带来一种处理长文本的Transformer变种——Transf…

SQS (Simple Queue Service)简介

mazon Simple Queue Service (SQS)是一种完全托管的消息队列服务&#xff0c;可以让你分离和扩展微服务、分布式系统和无服务应用程序。 在讲解SQS之前&#xff0c;首先让我们了解一下什么是消息队列。 消息队列 还是举一个电商的例子&#xff0c;一个用户在电商网站下单后付…

【LeetCode每日一题:[面试题 17.05] 字母与数字-前缀和+Hash表】

题目描述 给定一个放有字母和数字的数组&#xff0c;找到最长的子数组&#xff0c;且包含的字母和数字的个数相同。 返回该子数组&#xff0c;若存在多个最长子数组&#xff0c;返回左端点下标值最小的子数组。若不存在这样的数组&#xff0c;返回一个空数组。 示例 1: 输入…

LDGRB-01 3AFE61320954P0001

LDGRB-01 3AFE61320954P0001变频器的作用_变频器工作原理变频器是把工频电源&#xff08;50Hz或60Hz&#xff09;变换成各种频率的交流电源&#xff0c;以实现电机的变速运行的设备&#xff0c;其中控制电路完成对主电路的控制&#xff0c;整流电路将交流电变换成直流电&#x…

教育培训机构屡遭投诉?湖南中创教育给出三点建议

在中消协发布的《2021年校外教育培训领域消费者权益保护报告》中&#xff0c;受新冠疫情以及校外教育培训行业治理政策的冲击&#xff0c;我国校外教育培训领域消费维权舆情及消费者投诉呈上升趋势。2021年全国消协组织受理有关校外教育培训的投诉案件共80,528件&#xff0c;同…

探索N-gram模型:语言模型中的基础算法

❤️觉得内容不错的话&#xff0c;欢迎点赞收藏加关注&#x1f60a;&#x1f60a;&#x1f60a;&#xff0c;后续会继续输入更多优质内容❤️&#x1f449;有问题欢迎大家加关注私戳或者评论&#xff08;包括但不限于NLP算法相关&#xff0c;linux学习相关&#xff0c;读研读博…

Proteus8.15安装教程

1、解压Proteus8.15 安装包&#xff0c;然后双击进去&#xff0c;找到setup文件&#xff0c;右键&#xff0c;以管理员身份运行。 2、需要安装一些插件&#xff0c;点击“next”。把插件安装完成。 点击“finfish” 点击“install” 点击“Cancel” 3、如果没有上面步骤&…

零拷贝技术

1. 零拷贝技术 零拷贝就是一种避免 CPU 将数据从一块存储拷贝到另外一块存储的技术。针对操作系统中的设备驱动程序、文件系统以及网络协议堆栈而出现的各种零拷贝技术极大地提升了特定应用程序的性能&#xff0c;并且使得这些应用程序可以更加有效地利用系统资源。这种性能的…

latex使用笔记

在线表格转latex格式:https://tablesgenerator.com/ 在线公式转latex格式:https://latex.91maths.com/ 1、希腊字母 eg.σ 2、% 3、大小写罗马数字 大写罗马数字&#xff1a;\uppercase\expandafter{\romannumeral} 在romannumeral后加上相应的数字即可。 eg.Ⅱ \uppercas…

Python每日一练(20230311)

目录 1. 合并两个有序数组 2. 二叉树的右视图 3. 拼接最大数 &#x1f31f; 每日一练刷题专栏 C/C 每日一练 ​专栏 Python 每日一练 专栏 1. 合并两个有序数组 给你两个有序整数数组 nums1 和 nums2&#xff0c;请你将 nums2 合并到 nums1 中&#xff0c;使 nums1 成为…

SpringSecurity第一讲

目录 一、SpringSecurity01 1.2 什么是会话 1.2.1 基于session的认证 1.2.2 基于ToKen的认证 1.3 什么是授权 1.3.1 为什么要授权 1.3.2 SpringSecurity简介 1.4 SpringSecurity入门 1.4.1 pom文件 1.4.2 主启动类 1.4.3 创建控制层 1.4.4 启动项目进行测试 1.5 自…

工作三年,月薪不到20k,软件测试工程师,担心被应届生取代

工作了3年&#xff0c;一个月工资不到20K&#xff0c;担心被应届毕业生取代&#xff01; 互联网的快速发展伴随着员工适者生存的加速。几年是一条分界线。如果人们的能力和体力不够&#xff0c;他们就会被淘汰。生动的工作生活让许多人焦虑不安。 最近&#xff0c;一名来自211本…

Java分布式事务(五)

前言 随着互联网的快速发展&#xff0c;软件系统由原来的单体应用转变为分布式应用&#xff0c;下图描述了单体应用向微服务的演变。 文章目录&#x1f525;分布式事务处理-认识分布式事物&#x1f525;分布式架构的理论知识-CAP理论&#x1f525;分布式事务处理-分布式事务产…

查询校园网是否支持IPv6绕过校园网

方法一、连接校园网&#xff0c;登录认证网络可用后返回WIFI页面点击校园网WIFI的属性查看是否有IPV6地址 本地的IPV6地址不算哦 &#xff08;一般IPv6地址都是数字开头&#xff0c;fe80开头的都是本地IPv6地址是没用的&#xff09;方法二、连接校园网&#xff0c;登录认证网络…