我正在为 MVC Web 应用程序开发缓存管理器。对于这个应用程序,我有一些构建成本很高的非常大的对象。在应用程序的生命周期中,我可能需要根据用户请求创建几个这样的对象。构建后,用户将使用对象中的数据,从而导致许多读取操作。有时,我需要更新缓存对象中的一些次要数据点(创建和替换会花费太多时间)。
下面是我创建的一个缓存管理器类来帮助我。除了基本的线程安全之外,我的目标是:
- 允许对一个对象进行多次读取,但在更新请求时锁定对该对象的所有读取
- 如果对象不存在,请确保仅创建 1 次(请记住,这是一个漫长的构建操作)。
允许缓存存储许多对象,并为每个对象维护一个锁(而不是为所有对象一个锁)。
public class CacheManager { private static readonly ObjectCache Cache = MemoryCache.Default; private static readonly ConcurrentDictionary<string, ReaderWriterLockSlim> Locks = new ConcurrentDictionary<string, ReaderWriterLockSlim>(); private const int CacheLengthInHours = 1; public object AddOrGetExisting(string key, Func<object> factoryMethod) { Locks.GetOrAdd(key, new ReaderWriterLockSlim()); var policy = new CacheItemPolicy { AbsoluteExpiration = DateTimeOffset.Now.AddHours(CacheLengthInHours) }; return Cache.AddOrGetExisting (key, new Lazy<object>(factoryMethod), policy); } public object Get(string key) { var targetLock = AcquireLockObject(key); if (targetLock != null) { targetLock.EnterReadLock(); try { var cacheItem = Cache.GetCacheItem(key); if(cacheItem!= null) return cacheItem.Value; } finally { targetLock.ExitReadLock(); } } return null; } public void Update<T>(string key, Func<T, object> updateMethod) { var targetLock = AcquireLockObject(key); var targetItem = (Lazy<object>) Get(key); if (targetLock == null || key == null) return; targetLock.EnterWriteLock(); try { updateMethod((T)targetItem.Value); } finally { targetLock.ExitWriteLock(); } } private ReaderWriterLockSlim AcquireLockObject(string key) { return Locks.ContainsKey(key) ? Locks[key] : null; } }
我是否在保持线程安全的同时实现了我的目标?你们都看到了实现我的目标的更好方法吗?
谢谢!
更新:所以这里的底线是我真的试图在一个领域做太多事情。出于某种原因,我确信在管理缓存的同一类中管理 Get / Update 操作是个好主意。在查看了 Groo 的解决方案并重新思考了这个问题之后,我能够进行大量的重构,从而消除了我面临的这个问题。