功能要求
使用C#编写一个键值结构进程内缓存,实现数据缓存的基本操作。 这里用到的知识点如下:
-值缓存实现
说明:这里创建一个基于.net 6平台的控制台项目。
导入命名空间()
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
在.cs类中编写实现方法
///
/// 自定义内存缓存助手
///
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
///
/// 缓存字典 => 【key|value|time】
///
private static volatile ConcurrentDictionary<string, KeyValuePair<object, DateTime?>> _CacheDictionary = new();
///
/// 1.主动过期
///
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);
}
}
}
}
});
}
///
/// 索引器
///
///
///
public object this[string key]
{
get => _CacheDictionary[key];
set => _CacheDictionary[key] = new KeyValuePair<object, DateTime?>(value, null);
}
///
/// 设置相对过期缓存
///
/// 键
/// 数据包
/// 相对过期时间
public void Set(string key, object data, int seconds)
{
var expirationTime = DateTime.Now.AddSeconds(seconds);
_CacheDictionary[key] = new KeyValuePair<object, DateTime?>(data, expirationTime);
}
///
/// 设置绝对过期缓存
///
/// 键<
/// 数据包
public void Set(string key, object data)
{
this[key] = data; // 下面代码等效
// _CacheDictionary[key] = new KeyValuePair
}
///
/// 通过key获取缓存value
/// 2.被动过期
///
///
///
///
public T? Get(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;
}
///
/// 获取所有的key
///
///
public ICollection<string> GetKeys() => _CacheDictionary.Keys;
///
/// 获取缓存个数
///
///
public int Count()
{
int count = 0;
if (_CacheDictionary != null)
{
count = _CacheDictionary.Count;
}
return count;
}
///
/// 删除指定key的value
///
///
public void Remove(string key)
{
if (Exist(key))
{
_CacheDictionary.TryRemove(key, out KeyValuePair<object, DateTime?> value);
}
}
///
/// 清空所有缓存
///
public void Cleaner()
{
if (_CacheDictionary != null && _CacheDictionary.Count > 0)
{
foreach (var key in _CacheDictionary.Keys)
{
_CacheDictionary.TryRemove(key, out KeyValuePair<object, DateTime?> value);
}
}
}
///
/// 2.被动过期,保证任何过期缓存都无法取值
/// 检查key是否存在
///
///
///
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)}");
}
这里的代码中,我们添加了三组键值数据,其中一组不设置过期时间,另外两组设置过期时间。 保存数据后,分别打印缓存中保存的数据,在第二次缓存打印之前,先让线程休眠等待5秒(5000毫秒),并注意控制台输出的信息。
开始测试
从控制台输出的信息可以看到key=key2的值为空,这说明我们内部调用Exist方法已经生效,key2的值缓存的有效时间为3秒。 当第二次打印出信息时,此时已经休眠了5秒,key2中存储的内存数据已经超时,而key3中存储的值有效期为6秒,没有超时,这样就可以一一获取对应的内存数据。
开始测试
通过上面的demo演示,我们实现了一个自定义的进程内缓存助手,可以很方便的导入到项目中使用。
portant;overflow-wrap: break-word !important;">portant;overflow-wrap: break-word !important;">portant;overflow-wrap: break-word !important;">portant;overflow-wrap: break-word !important;">推荐阅读: portant;overflow-wrap: break-word !important;">inktype="text" imgurl="" imgdata="null" data-itemshowtype="11" tab="innerlink" data-linktype="2">微软开源了一个 助力开发LLM 加持的应用的 工具包 semantic-kernel portant;overflow-wrap: break-word !important;">inktype="text" imgurl="" imgdata="null" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="outline: 0px;-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">【译】使用 ChatGPT 和 Azure Cosmos DB 构建智能应用程序 portant;overflow-wrap: break-word !important;">inktype="text" imgurl="" imgdata="null" data-itemshowtype="11" tab="innerlink" data-linktype="2" hasload="1" style="outline: 0px;-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">实际体验文心一言 VS ChatGPT portant;overflow-wrap: break-word !important;">inktype="text" imgurl="" imgdata="null" data-itemshowtype="11" tab="innerlink" data-linktype="2" hasload="1" style="outline: 0px;-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">选择 .NET 的 n 个理由 portant;overflow-wrap: break-word !important;">inktype="text" imgurl="" imgdata="null" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="outline: 0px;-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">一个.NET开发的开源神级录屏软件 portant;overflow-wrap: break-word !important;">inktype="text" imgurl="" imgdata="null" data-itemshowtype="0" tab="innerlink" data-linktype="2" hasload="1" style="outline: 0px;-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">inktype="text" imgurl="" imgdata="null" data-itemshowtype="11" tab="innerlink" data-linktype="2" hasload="1" style="outline: 0px;-webkit-tap-highlight-color: rgba(0, 0, 0, 0);cursor: pointer;max-width: 100%;box-sizing: border-box !important;overflow-wrap: break-word !important;">跨平台`ChatGpt` 客户端 portant;overflow-wrap: break-word !important;">点击下方卡片关注DotNet NB
portant;overflow-wrap: break-word !important;">一起交流学习
portant;overflow-wrap: break-word !important;">
portant;overflow-wrap: break-word !important;"> portant;overflow-wrap: break-word !important;">▲ 点击上方卡片关注DotNet NB,一起交流学习
portant;overflow-wrap: break-word !important;">请在公众号后台
portant;overflow-wrap: break-word !important;">回复 【路线图】获取.NET 2021开发者路线图 portant;overflow-wrap: break-word !important;">回复 【原创内容】获取公众号原创内容 portant;overflow-wrap: break-word !important;">回复 【峰会视频】获取.NET Conf开发者大会视频 portant;overflow-wrap: break-word !important;">回复 【个人简介】获取作者个人简介 portant;overflow-wrap: break-word !important;">回复 【年终总结】获取作者年终总结 portant;overflow-wrap: break-word !important;">回复 【加群】加入DotNet NB 交流学习群 portant;overflow-wrap: break-word !important;"> portant;overflow-wrap: break-word !important;">长按识别下方二维码,或点击阅读原文。和我一起,交流学习,分享心得。 portant;overflow-wrap: break-word !important;"> portant;overflow-wrap: break-word !important;">
portant;overflow-wrap: break-word !important;">
portant;overflow-wrap: break-word !important;">