关于全局缓存的一种简单实现方法
缓存,在.Net系统开发中,经常使用到。如果,让你自己去实现,你会怎么做呢。
开始编码前思考:
1、肯定 是要 根据 key 去查询对应value,so 应该是List<KeyValuePair> 这类集合做缓存载体;
2、肯定是要全局公用,so 缓存的列表应该唯一;
3、应该有支持并发的能力,so 实现过程中肯定要加锁;
4、应该可以支持过期,自动 or 手动,so 应该 可以添加过期时间;
好的,基于以上3点,设计的类应该需要是单体(单例)模式,查询、删除key时应该lock 缓存载体,可以有定时清除过期缓存项的能力;
1 public static class CacheHelper 2 { 3 private static int RestoreCacheCount = 20000; 4 private static DateTime LastRestoreCachTime; 5 private static Dictionary<string, MyCacheEntity> CacheEntryDictionary; 6 private static readonly object objLock = new object(); 7 8 static CacheHelper() 9 { 10 if (CacheEntryDictionary == null) 11 { 12 lock (objLock) 13 { 14 if (CacheEntryDictionary == null) 15 { 16 CacheEntryDictionary = new Dictionary<string, MyCacheEntity>(); 17 LastRestoreCachTime = DateTime.Now; 18 System.Threading.Tasks.Task.Factory.StartNew(() => RestoreCache()); 19 } 20 } 21 } 22 } 23 24 public static void Set<T>(string key, T entity, double timeout) 25 { 26 if (string.IsNullOrEmpty(key)) 27 throw new ArgumentNullException(nameof(key)); 28 if (entity == null) 29 throw new ArgumentNullException(nameof(entity)); 30 var expiredTime = DateTime.Now.AddSeconds(timeout); 31 lock (objLock) 32 { 33 if (DateTime.Now.Subtract(expiredTime).TotalSeconds >= 0) 34 { 35 if (CacheEntryDictionary.ContainsKey(key)) 36 CacheEntryDictionary.Remove(key); 37 } 38 else 39 { 40 CacheEntryDictionary[key] = new MyCacheEntity { Value = entity, ExpiredTime = expiredTime }; 41 } 42 } 43 } 44 45 public static T Get<T>(string key) 46 { 47 if (string.IsNullOrEmpty(key)) 48 throw new ArgumentNullException(nameof(key)); 49 var value = default(T); 50 lock (objLock) 51 { 52 var exist = CacheEntryDictionary.ContainsKey(key); 53 if (!exist) return value; 54 var obj = CacheEntryDictionary[key]; 55 if (obj == null) 56 { 57 CacheEntryDictionary.Remove(key); 58 return value; 59 } 60 if (obj.ExpiredTime == DateTime.MinValue || DateTime.Now.Subtract(obj.ExpiredTime).TotalSeconds >= 0) 61 { 62 CacheEntryDictionary.Remove(key); 63 return value; 64 } 65 return (T)Convert.ChangeType(obj.Value, typeof(T)); 66 } 67 } 68 69 public static void Remove(string key) 70 { 71 if (string.IsNullOrEmpty(key)) 72 throw new ArgumentNullException(nameof(key)); 73 try 74 { 75 lock (objLock) 76 { 77 var exist = CacheEntryDictionary.ContainsKey(key); 78 if (!exist) return; 79 CacheEntryDictionary.Remove(key); 80 } 81 } 82 catch (Exception e) 83 { 84 throw e; 85 } 86 } 87 88 public static void Clear() 89 { 90 try 91 { 92 lock (objLock) 93 { 94 CacheEntryDictionary.Clear(); 95 LastRestoreCachTime = DateTime.Now; 96 } 97 } 98 catch (Exception e) 99 { 100 throw e; 101 } 102 } 103 104 public static int Count => CacheEntryDictionary?.Count ?? 0; 105 106 private static void RestoreCache() 107 { 108 while (true) 109 { 110 if (CacheEntryDictionary.Keys.Count < 1) return; 111 try 112 { 113 bool isEnter = false; 114 Monitor.TryEnter(CacheEntryDictionary, 1000, ref isEnter); 115 if (isEnter) 116 { 117 if (CacheEntryDictionary.Count > RestoreCacheCount && DateTime.Now.Subtract(LastRestoreCachTime).TotalMinutes > 1) 118 { 119 var keys = CacheEntryDictionary.Where(m => m.Value.ExpiredTime < DateTime.Now).Select(m => m.Key).ToList(); 120 foreach (var key in keys) 121 { 122 CacheEntryDictionary.Remove(key); 123 } 124 LastRestoreCachTime = DateTime.Now; 125 } 126 } 127 } 128 finally 129 { 130 Monitor.Exit(CacheEntryDictionary); 131 } 132 Thread.Sleep(1000 * 6); 133 } 134 } 135 } 136 public class MyCacheEntity 137 { 138 public object Value { get; set; } 139 public DateTime ExpiredTime { get; set; } 140 }
如果想要 key 不区分大小写,可以 在构造函数中 设置
CacheEntryDictionary = new Dictionary<string, MyCacheEntity>(StringComparer.OrdinalIgnoreCase);
你可能会有疑问,为什么不用 线程安全的字典 ConcurrentDictionary 呢?
当然可以将 Dictionary 改为 ConcurrentDictionary 没有任何问题,只是我想要自己手动实现加减速的过程;ConcurrentDictionary 字典内部也是调用Monitor 进行的加减锁的过程;
如果想要在缓存中实现 缓存文件,并且文件内容改变同步修改缓存的内容呢?
修改 以上方法 加入
1、添加缓存文件
2、刷新缓存文件内容,定时过期、监控文件内容更改等;
相关代码;
1 public static class CacheHelper 2 { 3 private static int RestoreCacheCount = 20000; 4 private static DateTime LastRestoreCachTime; 5 private static Dictionary<string, MyCacheEntity> CacheEntryDictionary; 6 private static readonly object objLock = new object(); 7 private static Dictionary<string, MyCacheEntity> FileCacheEntryDictionary; 8 9 static CacheHelper() 10 { 11 if (CacheEntryDictionary == null) 12 { 13 lock (objLock) 14 { 15 if (CacheEntryDictionary == null) 16 { 17 CacheEntryDictionary = new Dictionary<string, MyCacheEntity>(StringComparer.OrdinalIgnoreCase); 18 LastRestoreCachTime = DateTime.Now; 19 System.Threading.Tasks.Task.Factory.StartNew(() => RestoreCache()); 20 } 21 if (FileCacheEntryDictionary == null) 22 { 23 FileCacheEntryDictionary = new Dictionary<string, MyCacheEntity>(); 24 System.Threading.Tasks.Task.Factory.StartNew(() => ReFreshFileCache()); 25 } 26 } 27 } 28 } 29 30 public static void Set<T>(string key, T entity, double timeout) 31 { 32 if (string.IsNullOrEmpty(key)) 33 throw new ArgumentNullException(nameof(key)); 34 if (entity == null) 35 throw new ArgumentNullException(nameof(entity)); 36 var expiredTime = DateTime.Now.AddSeconds(timeout); 37 lock (objLock) 38 { 39 if (DateTime.Now.Subtract(expiredTime).TotalSeconds >= 0) 40 { 41 if (CacheEntryDictionary.ContainsKey(key)) 42 CacheEntryDictionary.Remove(key); 43 } 44 else 45 { 46 CacheEntryDictionary[key] = new MyCacheEntity { Value = entity, ExpiredTime = expiredTime }; 47 } 48 } 49 } 50 51 public static void SetFile(string key, string filePath, Encoding encoding, double timeout) 52 { 53 if (string.IsNullOrEmpty(key)) 54 throw new ArgumentNullException(nameof(key)); 55 if (!File.Exists(filePath)) 56 throw new FileNotFoundException(filePath); 57 var expiredTime = DateTime.Now.AddSeconds(timeout); 58 lock (objLock) 59 { 60 if (DateTime.Now.Subtract(expiredTime).TotalSeconds >= 0) 61 { 62 if (CacheEntryDictionary.ContainsKey(key)) 63 CacheEntryDictionary.Remove(key); 64 } 65 else 66 { 67 FileInfo fileInfo = new FileInfo(filePath); 68 string value = null; 69 using (var stream = new StreamReader(filePath, encoding)) 70 { 71 value = stream.ReadToEnd(); 72 } 73 CacheEntryDictionary[key] = new MyCacheEntity 74 { 75 Value = value, 76 ExpiredTime = expiredTime 77 }; 78 FileCacheEntryDictionary[key] = new MyCacheEntity 79 { 80 Value = new FileCacheOption 81 { 82 FilePath = filePath, 83 FileSize = fileInfo.Length, 84 LastWriteTime = fileInfo.LastWriteTime, 85 Encoding = encoding 86 }, 87 ExpiredTime = expiredTime 88 }; 89 } 90 } 91 } 92 93 public static T Get<T>(string key) 94 { 95 if (string.IsNullOrEmpty(key)) 96 throw new ArgumentNullException(nameof(key)); 97 var value = default(T); 98 lock (objLock) 99 { 100 var exist = CacheEntryDictionary.ContainsKey(key); 101 if (!exist) return value; 102 var obj = CacheEntryDictionary[key]; 103 if (obj == null) 104 { 105 CacheEntryDictionary.Remove(key); 106 return value; 107 } 108 if (obj.ExpiredTime == DateTime.MinValue || DateTime.Now.Subtract(obj.ExpiredTime).TotalSeconds >= 0) 109 { 110 CacheEntryDictionary.Remove(key); 111 return value; 112 } 113 return (T)Convert.ChangeType(obj.Value, typeof(T)); 114 } 115 } 116 117 public static void Remove(string key) 118 { 119 if (string.IsNullOrEmpty(key)) 120 throw new ArgumentNullException(nameof(key)); 121 try 122 { 123 lock (objLock) 124 { 125 var exist = CacheEntryDictionary.ContainsKey(key); 126 if (!exist) return; 127 CacheEntryDictionary.Remove(key); 128 if (FileCacheEntryDictionary.ContainsKey(key)) 129 FileCacheEntryDictionary.Remove(key); 130 } 131 } 132 catch (Exception e) 133 { 134 throw e; 135 } 136 } 137 138 public static void Clear() 139 { 140 try 141 { 142 lock (objLock) 143 { 144 CacheEntryDictionary.Clear(); 145 FileCacheEntryDictionary.Clear(); 146 LastRestoreCachTime = DateTime.Now; 147 } 148 } 149 catch (Exception e) 150 { 151 throw e; 152 } 153 } 154 155 public static int Count => CacheEntryDictionary?.Count ?? 0; 156 157 private static void RestoreCache() 158 { 159 while (true) 160 { 161 if (CacheEntryDictionary.Keys.Count < 1) return; 162 try 163 { 164 bool isEnter = false; 165 Monitor.TryEnter(CacheEntryDictionary, 1000, ref isEnter); 166 if (isEnter) 167 { 168 if (CacheEntryDictionary.Count > RestoreCacheCount && DateTime.Now.Subtract(LastRestoreCachTime).TotalMinutes > 1) 169 { 170 var keys = CacheEntryDictionary.Where(m => m.Value.ExpiredTime < DateTime.Now).Select(m => m.Key).ToList(); 171 foreach (var key in keys) 172 { 173 CacheEntryDictionary.Remove(key); 174 } 175 LastRestoreCachTime = DateTime.Now; 176 } 177 } 178 } 179 finally 180 { 181 Monitor.Exit(CacheEntryDictionary); 182 } 183 184 Thread.Sleep(1000 * 6); 185 } 186 } 187 188 private static void ReFreshFileCache() 189 { 190 while (true) 191 { 192 if (FileCacheEntryDictionary.Keys.Count < 1) return; 193 try 194 { 195 bool isEnter = false; 196 Monitor.TryEnter(FileCacheEntryDictionary, 100, ref isEnter); 197 if (isEnter && FileCacheEntryDictionary.Keys.Count > 0) 198 { 199 var keys = FileCacheEntryDictionary.Keys.ToList(); 200 foreach (var key in keys) 201 { 202 var value = FileCacheEntryDictionary[key]; 203 if (value.ExpiredTime <= DateTime.Now) 204 { 205 FileCacheEntryDictionary.Remove(key); 206 continue; 207 } 208 var fileCacheOption = value.Value as FileCacheOption; 209 if (fileCacheOption == null) 210 { 211 FileCacheEntryDictionary.Remove(key); 212 continue; 213 } 214 if (!File.Exists(fileCacheOption.FilePath)) 215 { 216 FileCacheEntryDictionary.Remove(key); 217 CacheEntryDictionary.Remove(key); 218 continue; 219 } 220 FileInfo fileInfo = new FileInfo(fileCacheOption.FilePath); 221 if (fileInfo.Length != fileCacheOption.FileSize || fileInfo.LastWriteTime != fileCacheOption.LastWriteTime) 222 { 223 fileCacheOption.LastWriteTime = fileInfo.LastWriteTime; 224 fileCacheOption.FileSize = fileInfo.Length; 225 FileCacheEntryDictionary[key] = new MyCacheEntity { Value = fileCacheOption, ExpiredTime = value.ExpiredTime }; 226 using (var stream = new StreamReader(fileCacheOption.FilePath, fileCacheOption.Encoding)) 227 { 228 Set(key, stream.ReadToEnd(), (int)value.ExpiredTime.Subtract(DateTime.Now).TotalSeconds); 229 } 230 } 231 } 232 } 233 } 234 finally 235 { 236 Monitor.Exit(FileCacheEntryDictionary); 237 } 238 239 Thread.Sleep(100); 240 } 241 } 242 } 243 public class MyCacheEntity 244 { 245 public object Value { get; set; } 246 public DateTime ExpiredTime { get; set; } 247 } 248 249 public class FileCacheOption 250 { 251 public string FilePath { get; set; } 252 public long FileSize { get; set; } 253 public DateTime LastWriteTime { get; set; } 254 public Encoding Encoding { get; set; } 255 }
其实上边的文件监控 可以用 FileSystemWatcher 来做文件监控,。只是我做测试的时候,在触发多个事件读取文件内容时,会报文件被占用,然后就是在centos 下,编辑文件后保存时,会同时触发 Created、Changed 两个事件,在windows 下不存在这种情况,可能是我的方法设置有问题吧。
转载于:https://www.cnblogs.com/flyfishing/articles/Global_CacheHelper.html
关于全局缓存的一种简单实现方法相关推荐
- 有没有一种简单的方法可以按值删除列表元素?
a = [1, 2, 3, 4] b = a.index(6)del a[b] print a 上面显示了以下错误: Traceback (most recent call last):File &q ...
- 一百种简单整人方法_一种非常简单的用户故事方法
一百种简单整人方法 User stories are a great way to plan development work. In theory. But how do you avoid get ...
- java按两列输出_有没有一种简单的方法可以将两列输出到Java中的控制台? - java...
如标题所述,是否有一种简单的方法可以将两列输出到Java中的控制台? 我知道\t,但是在使用printf时,我还没有找到基于特定列进行空间分配的方法. 参考方案 使用宽度和精度说明符,将其设置为相同的 ...
- 上微信怎么同时用计算机,电脑端微信双开,教你两种简单的方法,上手即用!...
原标题:电脑端微信双开,教你两种简单的方法,上手即用! 微信现在已不单单是社交软件了,如今已成了工作必备软件,每天上班微信电脑端一登,传个文件,发个群通知,实在方便. 但是很多人都不止有一个微信号,电 ...
- 电脑端微信双开,教你两种简单的方法,上手即用!
电脑端微信双开,教你两种简单的方法,上手即用! https://kuaibao.qq.com/s/20181117A0GAZF00?refer=spider 微信现在已不单单是社交软件了,如今已成了工 ...
- 9种简单的方法来使用社交媒体SEO
9种简单的方法来使用社交媒体SEO 搜索引擎优化和社交媒体营销是两种策略,可以帮助提升品牌知名度并推广您的业务.他们创造吸引人的身份,自然吸引游客. 由于社交媒体主要依靠高质量的内容和强大的可见品牌存 ...
- python 加一个月 日期,有没有一种简单的方法可以在Python中将datetime对象增加一个月?...
本问题已经有最佳答案,请猛点这里访问. 所以我试图找到一种方法,将日期时间对象增加一个月.然而,根据这个问题,这似乎不是那么简单. 我希望有这样的事情: import datetime as dt n ...
- android 层级边框,有没有一种简单的方法可以在Android视图的顶部和底部添加边框?...
有没有一种简单的方法可以在Android视图的顶部和底部添加边框? 我有一个TextView,我想沿其顶部和底部边框添加黑色边框. 我尝试将android:drawableTop和android:dr ...
- 三角网格参数化几种简单的方法比较
三角网格参数化可归结为这样一个问题:给定一个由空间点集组成的三角网格和一个二维参数域.通常为平面或者球面.求一个参数域上的点 P∗iP_i^* 到网格上的点pip_i的一一映射.使得参数域上的网格与原 ...
最新文章
- mysql 113_MySQL教程113-MySQL流程控制语句
- 我们和计算机系的老教授聊了聊TCP优化与产学矛盾
- Redis 的应用场景
- 链表之单链表的反转总结
- C++不定长字符串比较合理且简单的解决方法
- (转)OL2中设置鼠标的样式
- python 求 gamma 分布_python如何生成各种随机分布图
- JSON文件导入Unity3d中是空的的问题
- qt的opengl开发(qopenglwidget)(初始化,画线,平移,局部缩放)2d开发
- wpe修改充值_wpe实现网络游戏修改
- C++之struct
- 英语汉语对比学习:名词(一)
- Vue多个元素的过渡
- 电信光猫 中兴ZXHN F412破解
- CodeForces 964A Splits
- 什么是一对一电脑无人直播?
- wps英文参考文献怎么对齐_wps英文参考文献怎么对齐_【论文攻略】排版技巧——如何用 Word 编辑参考文献......
- jack分享的1-3开wifi 零火版本智能开关解决方案
- linux运行gpg软件,Linux实用工具之GPG
- NFTFi赛道版图概览
热门文章
- @builder注解_SpringBoot 整合SpringSecurity示例实现前后分离权限注解+JWT登录认证
- 自定义Mybatis框架
- 移动端设置html的字体尺寸,移动端开发元素及字体尺寸适配基础知识
- inkscape生成g代码_三点二. 量子对抗生成网络 (Quantum GAN)
- Pytorch代码函数笔记
- python获取一个月之前日期_利用python获取当前日期前后N天或N月日期的方法示例...
- 修改value_Python | 快速修改或命名N个文件夹名称,你会吗?
- aes子密钥生成c语言_一种基于流密码算法的子密钥生成方法与流程
- visual2019没有勾选的在如何加入_发票管理系统完成升级,勾选认证平台改头换面,你所有的问题都在这里!...
- C语言代码注释必须用/**/ , 你没看错~