zoukankan      html  css  js  c++  java
  • 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
    
        }
    }
    View Code

    为什么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;
        }
    }
    View Code

    如何使用:

    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;
    }
    View Code

     多线程并发测试:

    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);
            });
        }
    }
    View Code
  • 相关阅读:
    在美国贩卖早餐的小摊贩
    随感
    业内人士称游资3年前开始准备炒作糖价
    许志:量化宽松在即 美元迎来关键一周
    9月17日  逾200亿资金净流出 农行轰然长阴 好笑
    9月热钱流入环比加速 多为投机性资金
    错过了多次捞钱的机会
    20101012 期货盘面暴跌,亏损持仓
    致歉申明,现在《微电子工程师》可以正常下载了
    SemiId半导体型号识别器1.0发布
  • 原文地址:https://www.cnblogs.com/s0611163/p/15107258.html
Copyright © 2011-2022 走看看