C# 内存缓存工具类 MemoryCacheUtil

c#教程https://www.xin3721.com/eschool/CSharpxin3721/

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);
        });
    }
}



 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值