我正在使用下面的代码来缓存项目。这很基础。
我遇到的问题是,每次缓存一个项目时,代码锁定部分。因此每小时有大约一百万件物品到货……
您可能需要考虑使用 ReaderWriterLockSlim ,只有在需要时才能获得写锁。
运用 cacheLock.EnterReadLock(); 和 cacheLock.EnterWriteLock(); 应该大大提高性能。
cacheLock.EnterReadLock();
cacheLock.EnterWriteLock();
我给的链接甚至有一个缓存的例子,正是你需要的,我在这里复制:
public class SynchronizedCache { private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim(); private Dictionary<int, string> innerCache = new Dictionary<int, string>(); public int Count { get { return innerCache.Count; } } public string Read(int key) { cacheLock.EnterReadLock(); try { return innerCache[key]; } finally { cacheLock.ExitReadLock(); } } public void Add(int key, string value) { cacheLock.EnterWriteLock(); try { innerCache.Add(key, value); } finally { cacheLock.ExitWriteLock(); } } public bool AddWithTimeout(int key, string value, int timeout) { if (cacheLock.TryEnterWriteLock(timeout)) { try { innerCache.Add(key, value); } finally { cacheLock.ExitWriteLock(); } return true; } else { return false; } } public AddOrUpdateStatus AddOrUpdate(int key, string value) { cacheLock.EnterUpgradeableReadLock(); try { string result = null; if (innerCache.TryGetValue(key, out result)) { if (result == value) { return AddOrUpdateStatus.Unchanged; } else { cacheLock.EnterWriteLock(); try { innerCache[key] = value; } finally { cacheLock.ExitWriteLock(); } return AddOrUpdateStatus.Updated; } } else { cacheLock.EnterWriteLock(); try { innerCache.Add(key, value); } finally { cacheLock.ExitWriteLock(); } return AddOrUpdateStatus.Added; } } finally { cacheLock.ExitUpgradeableReadLock(); } } public void Delete(int key) { cacheLock.EnterWriteLock(); try { innerCache.Remove(key); } finally { cacheLock.ExitWriteLock(); } } public enum AddOrUpdateStatus { Added, Updated, Unchanged }; ~SynchronizedCache() { if (cacheLock != null) cacheLock.Dispose(); } }
我不知道怎么回事 MemoryCache.Default 已实施,或您是否可以控制它。 但总的来说,更喜欢使用 ConcurrentDictionary 过度 Dictionary 锁定在多线程环境中。
MemoryCache.Default
ConcurrentDictionary
Dictionary
GetFromCache 会成为
GetFromCache
ConcurrentDictionary<string, T> cache = new ConcurrentDictionary<string, T>(); ... cache.GetOrAdd("someKey", (key) => { var data = PullDataFromDatabase(key); return data; });
还有两件事需要注意。
而不是储蓄 T 作为字典的值,您可以定义类型
T
struct CacheItem<T> { public T Item { get; set; } public DateTime Expiry { get; set; } }
并将缓存存储为 CacheItem 具有定义的到期日。
CacheItem
cache.GetOrAdd("someKey", (key) => { var data = PullDataFromDatabase(key); return new CacheItem<T>() { Item = data, Expiry = DateTime.UtcNow.Add(TimeSpan.FromHours(1)) }; });
现在,您可以在异步线程中实现到期。
Timer expirationTimer = new Timer(ExpireCache, null, 60000, 60000); ... void ExpireCache(object state) { var needToExpire = cache.Where(c => DateTime.UtcNow >= c.Value.Expiry).Select(c => c.Key); foreach (var key in needToExpire) { cache.TryRemove(key, out CacheItem<T> _); } }
每分钟一次,您将搜索所有需要过期的缓存条目,然后将其删除。
运用 ConcurrentDictionary 保证同时读/写不会破坏字典或抛出异常。 但是,您仍然可能最终导致两次同时读取导致您从数据库中获取数据两次。
解决这个问题的一个巧妙方法是用字母包装字典的值 Lazy
Lazy
ConcurrentDictionary<string, Lazy<CacheItem<T>>> cache = new ConcurrentDictionary<string, Lazy<CacheItem<T>>>(); ... var data = cache.GetOrData("someKey", key => new Lazy<CacheItem<T>>(() => { var data = PullDataFromDatabase(key); return new CacheItem<T>() { Item = data, Expiry = DateTime.UtcNow.Add(TimeSpan.FromHours(1)) }; })).Value;
的 说明 强>
同 GetOrAdd 在同时请求的情况下,您最终可能会多次调用“从数据库获取,如果不在缓存中”委托多次。 然而, GetOrAdd 最终只会使用委托返回的一个值,并返回一个 Lazy ,你保证只有一个 Lazy 将被调用。
GetOrAdd