如何实现用于缓存的线程安全单例字典类?
线程安全的单例字典缓存实现方案
嘿,我帮你搞定这个线程安全的单例字典缓存类!这种需求在日常开发里太常见了——既要保证全局只有一个缓存实例,又得扛得住多线程同时操作的情况。直接给你上可运行的代码和详细解释吧:
完整手动锁实现版本
这个版本用经典的双重检查锁定保证单例的线程安全,同时通过手动加锁保护字典的操作:
using System; using System.Collections.Generic; using System.Threading; public class ThreadSafeSingletonCache { // volatile关键字确保实例状态对所有线程可见,避免指令重排序问题 private static volatile ThreadSafeSingletonCache _instance; // 用于线程同步的专属锁对象 private static readonly object _lockObj = new object(); // 内部存储缓存的普通字典 private readonly Dictionary<string, object> _cacheDictionary; // 私有构造函数,禁止外部直接实例化 private ThreadSafeSingletonCache() { _cacheDictionary = new Dictionary<string, object>(); } // 全局唯一访问入口 public static ThreadSafeSingletonCache Instance { get { // 第一次检查,避免每次获取实例都加锁,提升性能 if (_instance == null) { lock (_lockObj) { // 第二次检查,确保只有第一个进入锁的线程创建实例 if (_instance == null) { _instance = new ThreadSafeSingletonCache(); } } } return _instance; } } // 线程安全的添加/更新缓存 public void AddOrUpdate(string key, object value) { lock (_cacheDictionary) { if (_cacheDictionary.ContainsKey(key)) { _cacheDictionary[key] = value; } else { _cacheDictionary.Add(key, value); } } } // 线程安全的获取缓存 public bool TryGet(string key, out object value) { lock (_cacheDictionary) { return _cacheDictionary.TryGetValue(key, out value); } } // 线程安全的移除缓存 public bool Remove(string key) { lock (_cacheDictionary) { return _cacheDictionary.Remove(key); } } // 线程安全的清空缓存 public void Clear() { lock (_cacheDictionary) { _cacheDictionary.Clear(); } } } // 测试用的Main方法 class Program { static void Main(string[] args) { // 获取全局唯一缓存实例 var cache = ThreadSafeSingletonCache.Instance; // 添加缓存项 cache.AddOrUpdate("User:1", new { Id = 1, Name = "Alice" }); cache.AddOrUpdate("Product:100", new { Id = 100, Price = 99.99 }); // 获取并输出缓存项 if (cache.TryGet("User:1", out var user)) { Console.WriteLine($"获取到用户信息: {user}"); } // 移除指定缓存项 cache.Remove("Product:100"); // 清空所有缓存 cache.Clear(); } }
关键设计细节
- 双重检查锁定:这是线程安全单例的经典实现,既保证了全局唯一实例,又避免了每次获取实例都加锁的性能开销。
volatile关键字是关键,它能防止CPU指令重排序导致的多线程下实例未完全初始化就被访问的问题。 - 字典线程安全保护:.NET自带的
Dictionary<TKey,TValue>本身不是线程安全的,所以我们对所有字典操作都加了锁,确保同一时间只有一个线程能修改或读取字典。
简化版本(使用ConcurrentDictionary)
如果你的项目基于.NET Framework 4.0+或.NET Core,推荐直接用ConcurrentDictionary和Lazy<T>来实现,代码更简洁,性能也更优:
using System; using System.Collections.Concurrent; public class ThreadSafeSingletonCache { // Lazy<T>自带线程安全的延迟初始化,无需手动加锁 private static readonly Lazy<ThreadSafeSingletonCache> _lazyInstance = new Lazy<ThreadSafeSingletonCache>(() => new ThreadSafeSingletonCache()); // 内置线程安全的字典 private readonly ConcurrentDictionary<string, object> _cacheDictionary; private ThreadSafeSingletonCache() { _cacheDictionary = new ConcurrentDictionary<string, object>(); } public static ThreadSafeSingletonCache Instance => _lazyInstance.Value; public void AddOrUpdate(string key, object value) { // ConcurrentDictionary自带线程安全的增改方法 _cacheDictionary.AddOrUpdate(key, value, (existingKey, existingValue) => value); } public bool TryGet(string key, out object value) { return _cacheDictionary.TryGetValue(key, out value); } public bool Remove(string key) { return _cacheDictionary.TryRemove(key, out _); } public void Clear() { _cacheDictionary.Clear(); } }
这个版本里,Lazy<T>帮我们处理了单例的线程安全初始化,ConcurrentDictionary则内置了所有线程安全的操作方法,省去了手动加锁的麻烦,高并发场景下表现更好。
内容的提问来源于stack exchange,提问作者Ku ttan




