VB.net 2010 视频教程 VB.net 2010 视频教程 python基础视频教程
SQL Server 2008 视频教程 c#入门经典教程 Visual Basic从门到精通视频教程
当前位置:
首页 > C#教程 >
  • C# 内存缓存工具类 MemoryCacheUtil

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);
        });
    }
}
复制代码


原文链接:https://www.cnblogs.com/s0611163/p/15107258.html


相关教程