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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM