C# 内存缓存工具类 MemoryCacheUtil

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;
using System.Timers; namespace Utils
{
/// <summary>
/// 缓存
/// 缓存数据存储在内存中
/// 适用于CS项目,BS项目慎用
/// </summary>
public static class MemoryCacheUtil
{
#region 变量
/// <summary>
/// 内存缓存
/// </summary>
private static ConcurrentDictionary<string, CacheData> _cacheDict = new ConcurrentDictionary<string, CacheData>(); /// <summary>
/// 对不同的键提供不同的锁,用于读缓存
/// </summary>
private static ConcurrentDictionary<string, string> _dictLocksForReadCache = new ConcurrentDictionary<string, string>(); /// <summary>
/// 过期缓存检测Timer
/// </summary>
private static Timer _timerCheckCache;
#endregion #region 静态构造函数
static MemoryCacheUtil()
{
_timerCheckCache = new Timer();
_timerCheckCache.Interval = 60 * 1000;
_timerCheckCache.Elapsed += _timerCheckCache_Elapsed;
_timerCheckCache.Start();
}
#endregion #region 获取并缓存数据
/// <summary>
/// 获取并缓存数据
/// 高并发的情况建议使用此重载函数,防止重复写入内存缓存
/// </summary>
/// <param name="cacheKey">键</param>
/// <param name="func">在此方法中初始化数据</param>
/// <param name="expirationSeconds">缓存过期时间(秒),0表示永不过期</param>
/// <param name="refreshCache">立即刷新缓存</param>
public static T TryGetValue<T>(string cacheKey, Func<T> func, int expirationSeconds = 0, bool refreshCache = false)
{
lock (_dictLocksForReadCache.GetOrAdd(cacheKey, cacheKey))
{
object cacheValue = MemoryCacheUtil.GetValue(cacheKey);
if (cacheValue != null && !refreshCache)
{
return (T)cacheValue;
}
else
{
T value = func();
MemoryCacheUtil.SetValue(cacheKey, value, expirationSeconds);
return value;
}
}
}
#endregion #region SetValue 保存键值对
/// <summary>
/// 保存键值对
/// </summary>
/// <param name="key">缓存键</param>
/// <param name="value">值</param>
/// <param name="expirationSeconds">过期时间(秒),0表示永不过期</param>
internal static void SetValue(string key, object value, int expirationSeconds = 0)
{
try
{
CacheData data = new CacheData(key, value);
data.updateTime = DateTime.Now;
data.expirationSeconds = expirationSeconds; CacheData temp;
_cacheDict.TryRemove(key, out temp);
_cacheDict.TryAdd(key, data);
}
catch (Exception ex)
{
LogUtil.Error(ex, "MemoryCacheUtil写缓存错误");
}
}
#endregion #region GetValue 获取键值对
/// <summary>
/// 获取键值对
/// </summary>
internal static object GetValue(string key)
{
try
{
CacheData data;
if (_cacheDict.TryGetValue(key, out data))
{
if (data.expirationSeconds > 0 && DateTime.Now.Subtract(data.updateTime).TotalSeconds > data.expirationSeconds)
{
CacheData temp;
_cacheDict.TryRemove(key, out temp);
return null;
}
return data.value;
}
return null;
}
catch (Exception ex)
{
LogUtil.Error(ex, "MemoryCacheUtil读缓存错误");
return null;
}
}
#endregion #region Delete 删除缓存
/// <summary>
/// 删除缓存
/// </summary>
internal static void Delete(string key)
{
CacheData temp;
_cacheDict.TryRemove(key, out temp);
}
#endregion #region DeleteAll 删除全部缓存
/// <summary>
/// 删除全部缓存
/// </summary>
internal static void DeleteAll()
{
_cacheDict.Clear();
}
#endregion #region 过期缓存检测
/// <summary>
/// 过期缓存检测
/// </summary>
private static void _timerCheckCache_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
Task.Run(() =>
{
try
{
foreach (string cacheKey in _cacheDict.Keys.ToList())
{
CacheData data;
if (_cacheDict.TryGetValue(cacheKey, out data))
{
if (data.expirationSeconds > 0 && DateTime.Now.Subtract(data.updateTime).TotalSeconds > data.expirationSeconds)
{
CacheData temp;
string strTemp;
_cacheDict.TryRemove(cacheKey, out temp);
_dictLocksForReadCache.TryRemove(cacheKey, out strTemp);
}
}
}
}
catch (Exception ex)
{
LogUtil.Error(ex, "过期缓存检测出错");
}
});
}
#endregion }
}

为什么BS项目慎用?因为IIS会回收进程,所以需要注意一下。

为什么过期缓存检测遍历代码是foreach (string cacheKey in _cacheDict.Keys.ToList()),要使用ToList()?_cacheDict.Keys不是线程安全的,防止并发异常。

为什么加锁的代码是lock (_dictLocksForReadCache.GetOrAdd(cacheKey, cacheKey))?为了支持多线程并发,防止重复进入func函数。

CacheData类:

/// <summary>
/// 缓存数据
/// </summary>
[Serializable]
public class CacheData
{
/// <summary>
/// 键
/// </summary>
public string key { get; set; }
/// <summary>
/// 值
/// </summary>
public object value { get; set; }
/// <summary>
/// 缓存更新时间
/// </summary>
public DateTime updateTime { get; set; }
/// <summary>
/// 过期时间(秒),0表示永不过期
/// </summary>
public int expirationSeconds { get; set; } /// <summary>
/// 缓存数据
/// </summary>
/// <param name="key">缓存键</param>
/// <param name="value">值</param>
public CacheData(string key, object value)
{
this.key = key;
this.value = value;
}
}

如何使用:

private void button2_Click(object sender, EventArgs e)
{
List<string> list = MemoryCacheUtil.TryGetValue<List<string>>("cacheKey001", () =>
{
return QueryData();
});
} /// <summary>
/// 模拟从数据库查询数据
/// </summary>
private List<string> QueryData()
{
List<string> result = new List<string>(); for (int i = 0; i < 10; i++)
{
result.Add(i.ToString());
} return result;
}

多线程并发测试:

private void TestMemoryCache()
{
Log("开始");
for (int i = 0; i < 5; i++)
{
Task.Run(() =>
{
string str1 = MemoryCacheUtil.TryGetValue<string>("1", () =>
{
Thread.Sleep(2000);
Log("取数据1");
return "1";
});
Log(str1);
}); Task.Run(() =>
{
string str2 = MemoryCacheUtil.TryGetValue<string>("2", () =>
{
Thread.Sleep(2000);
Log("取数据2");
return "2";
});
Log(str2);
}); Task.Run(() =>
{
string str3 = MemoryCacheUtil.TryGetValue<string>("3", () =>
{
Thread.Sleep(2000);
Log("取数据3");
return "3";
});
Log(str3);
});
}
}

C# 内存缓存工具类 MemoryCacheUtil的更多相关文章

  1. 分享基于MemoryCache(内存缓存)的缓存工具类,C# B/S 、C/S项目均可以使用!

    using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Caching; usi ...

  2. Go/Python/Erlang编程语言对比分析及示例 基于RabbitMQ.Client组件实现RabbitMQ可复用的 ConnectionPool(连接池) 封装一个基于NLog+NLog.Mongo的日志记录工具类LogUtil 分享基于MemoryCache(内存缓存)的缓存工具类,C# B/S 、C/S项目均可以使用!

    Go/Python/Erlang编程语言对比分析及示例   本文主要是介绍Go,从语言对比分析的角度切入.之所以选择与Python.Erlang对比,是因为做为高级语言,它们语言特性上有较大的相似性, ...

  3. Cache【硬盘缓存工具类(包含内存缓存LruCache和磁盘缓存DiskLruCache)】

    版权声明:本文为HaiyuKing原创文章,转载请注明出处! 前言 内存缓存LruCache和磁盘缓存DiskLruCache的封装类,主要用于图片缓存. 效果图 代码分析 内存缓存LruCache和 ...

  4. php 缓存工具类 实现网页缓存

    php 缓存工具类 实现网页缓存 php程序在抵抗大流量访问的时候动态网站往往都是难以招架,所以要引入缓存机制,一般情况下有两种类型缓存 一.文件缓存 二.数据查询结果缓存,使用内存来实现高速缓存 本 ...

  5. thrift之TTransport层的内存缓存传输类TMemoryBuffer

    内存缓存是简单的在内存进行读写操作的一种传输,任何时候想在上面写入数据都是放入缓存中,任何时候读操作数据也是来至于缓存.内存缓存的分配使用c语言的malloc类函数,分配的长度是需要长度的两倍,需要考 ...

  6. redis缓存工具类

    import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis ...

  7. redis缓存工具类,提供序列化接口

    1.序列化工具类 package com.qicheshetuan.backend.util; import java.io.ByteArrayInputStream; import java.io. ...

  8. CookieUtils-浏览器缓存工具类

    package cn.yonyong.myproject.commons.utils; import javax.servlet.http.Cookie; import javax.servlet.h ...

  9. 缓存工具类CacheHelper

    代码: using System; using System.Collections.Generic; using System.Linq; using System.Text; using Syst ...

  10. 基于spring的redisTemplate的缓存工具类

    pom.xml文件添加 <!-- config redis data and client jar --><dependency> <groupId>org.spr ...

随机推荐

  1. 总结--flask部分

    Flask框架的诞生: Flask诞生于2010年,是Armin ronacher(人名)用Python语言基于Werkzeug工具箱编写的轻量级Web开发框架. Flask本身相当于一个内核,其他几 ...

  2. Batrix企业能力库之物流交易域能力建设实践

    简介 Batrix企业能力库,是京东物流战略级项目-技术中台架构升级项目的基础底座.致力于建立企业级业务复用能力平台,依托能力复用业务框架Batrix,通过通用能力/扩展能力的定义及复用,灵活支持业务 ...

  3. 【Servlet】两种配置

    web.xml中Servlet的注解 <servlet> <!-- servlet的内部名称,⾃定义 --> <servlet-name>类名</servle ...

  4. Codeforces Round 905 (Div. 3)

    Codeforces Round 905 (Div. 3) A. Morning 题意:操作:显示,向前走都为一次操作:目标:显示这四个数 思路:0->10,然后依次作差就行 #include ...

  5. Mybatis出现Caused by: net.sf.jsqlparser.parser.ParseException: ....异常

    今天在开发项目中遇到了一个奇怪的异常,记录一下把! 异常信息如下(截取了主要的部分) Caused by: net.sf.jsqlparser.parser.ParseException: Encou ...

  6. Charles对Android手机Https请求的抓包

    Charles对Android手机Https请求的抓包 • 前情提要: 本文只是对android手机进行抓包的描述,由于android手机系统原因,android7.0系统及以上需要在app中配置证书 ...

  7. [研究]SpringBoot-MybatisPlus-Dynamic(多数据源)

    SpringBoot-MybatisPlus-Dynamic(多数据源) 前言 ​ 基于工作上班累死了...打开自己电脑 不知道干些啥 就康康 PL 网站康康 更新了啥 ​ 咦~~~还挺多 看到了多数 ...

  8. Codeforces 918(div4)

    Codeforces 918(div4) Problem - A - Codeforces #include<bits/stdc++.h> using namespace std; con ...

  9. 通过印模生成电子印章-Java源代码

    以下代码是处理印模图片的核心代码,通过以下代码可以将公章图片转换为电子印章图片. 制作方式分为四步: 1.在白纸上加盖印章: 2.把加盖印章的白纸扫描,形成图片: 3.将图片通过下面的代码进行自动透明 ...

  10. 用C实现HashTable

    简述HashTable的原理 HashTable是一种数据结构,通过key可以直接的到value,查找值时间总为常数级别O(1). 原理 HashTable底层是使用了数组实现的.数组只要知道了索引, ...