在asp.net中lockingcaching的最佳方法是什么?
我知道在某些情况下,例如长时间运行的进程,为了避免其他用户对该资源的后续请求再次执行长进程而不是执行caching,lockingASP.NETcaching非常重要。
在ASP.NET中实现cachinglocking的最佳方式是什么?
这是基本模式:
- 检查caching中的值,如果可用则返回
- 如果该值不在caching中,则执行locking
- 在锁内部,再次检查caching,可能已被阻止
- 执行值查找并caching它
- 释放locking
在代码中,它看起来像这样:
private static object ThisLock = new object(); public string GetFoo() { // try to pull from cache here lock (ThisLock) { // cache was empty before we got the lock, check again inside the lock // cache is still empty, so retreive the value here // store the value in the cache here } // return the cached value here }
为了完整性,一个完整的例子看起来像这样。
private static object ThisLock = new object(); ... object dataObject = Cache["globalData"]; if( dataObject == null ) { lock( ThisLock ) { dataObject = Cache["globalData"]; if( dataObject == null ) { //Get Data from db dataObject = GlobalObj.GetData(); Cache["globalData"] = dataObject; } } } return dataObject;
只是为了回应Pavel说的话,我相信这是写它的最安全的方法
private T GetOrAddToCache<T>(string cacheKey, GenericObjectParamsDelegate<T> creator, params object[] creatorArgs) where T : class, new() { T returnValue = HttpContext.Current.Cache[cacheKey] as T; if (returnValue == null) { lock (this) { returnValue = HttpContext.Current.Cache[cacheKey] as T; if (returnValue == null) { returnValue = creator(creatorArgs); if (returnValue == null) { throw new Exception("Attempt to cache a null reference"); } HttpContext.Current.Cache.Add( cacheKey, returnValue, null, System.Web.Caching.Cache.NoAbsoluteExpiration, System.Web.Caching.Cache.NoSlidingExpiration, CacheItemPriority.Normal, null); } } } return returnValue; }
Craig Shoemaker在asp.netcaching方面做了一个很好的演示: http : //polymorphicpodcast.com/shows/webperformance/
我想出了以下扩展方法:
private static readonly object _lock = new object(); public static TResult GetOrAdd<TResult>(this Cache cache, string key, Func<TResult> action, int duration = 300) { TResult result; var data = cache[key]; // Can't cast using as operator as TResult may be an int or bool if (data == null) { lock (_lock) { data = cache[key]; if (data == null) { result = action(); if (result == null) return result; if (duration > 0) cache.Insert(key, result, null, DateTime.UtcNow.AddSeconds(duration), TimeSpan.Zero); } else result = (TResult)data; } } else result = (TResult)data; return result; }
我用@John Owen和@ user378380答案。 我的解决scheme允许您将int和bool值存储在caching中。
请纠正我是否有任何错误,或者是否可以写得更好一点。
我看到了一种最近叫做“正确的状态包存取模式”的模式,这似乎触及了这一点。
我修改了一下,以确保线程安全。
http://weblogs.asp.net/craigshoemaker/archive/2008/08/28/asp-net-caching-and-performance.aspx
private static object _listLock = new object(); public List List() { string cacheKey = "customers"; List myList = Cache[cacheKey] as List; if(myList == null) { lock (_listLock) { myList = Cache[cacheKey] as List; if (myList == null) { myList = DAL.ListCustomers(); Cache.Insert(cacheKey, mList, null, SiteConfig.CacheDuration, TimeSpan.Zero); } } } return myList; }
不需要locking整个caching实例,而只需要locking要插入的特定密钥。 即无需阻止访问女厕所,而你使用男厕所:)
下面的实现允许使用并发字典来locking特定的caching键。 这样,您可以同时为两个不同的键运行GetOrAdd(),但不能同时运行同一个键。
using System; using System.Collections.Concurrent; using System.Web; using System.Web.Caching; public static class CacheExtensions { private static ConcurrentDictionary<string, object> keyLocks = new ConcurrentDictionary<string, object>(); /// <summary> /// Get or Add the item to the cache /// </summary> public static T GetOrAdd<T>(this Cache cache, string key, Func<T> factory, int durationInSeconds, bool synchronised = true) where T : class { // Try and get value from the cache var value = cache.Get(key); if (value == null) { // If not yet cached, lock the key value and add to cache lock (keyLocks.GetOrAdd(key, new object())) { // Try and get from cache again in case it has been added in the meantime value = HttpRuntime.Cache.Get(key); if (value == null && (value = factory()) != null) { HttpRuntime.Cache.Insert(key, value, null, DateTime.Now.AddSeconds(durationInSeconds), Cache.NoSlidingExpiration, CacheItemPriority.Default, null); } // Remove temporary key lock object locker; keyLocks.TryRemove(key, out locker); } } return value as T; } }
来自CodeGuru的这篇文章解释了各种cachinglockingscheme以及ASP.NETcachinglocking的一些最佳实践:
在ASP.NET中同步高速caching访问
我写了一个解决这个特定问题的库: Rocks.Caching
此外,我已经详细地讨论了这个问题,并解释了为什么它在这里很重要。
我修改了@ user378380的代码以获得更大的灵活性。 现在不用返回TResult,而是按顺序返回接受不同types的对象。 还增加了一些参数的灵活性。 所有的想法都属于@ user378380。
private static readonly object _lock = new object(); //If getOnly is true, only get existing cache value, not updating it. If cache value is null then set it first as running action method. So could return old value or action result value. //If getOnly is false, update the old value with action result. If cache value is null then set it first as running action method. So always return action result value. //With oldValueReturned boolean we can cast returning object(if it is not null) appropriate type on main code. public static object GetOrAdd<TResult>(this Cache cache, string key, Func<TResult> action, DateTime absoluteExpireTime, TimeSpan slidingExpireTime, bool getOnly, out bool oldValueReturned) { object result; var data = cache[key]; if (data == null) { lock (_lock) { data = cache[key]; if (data == null) { oldValueReturned = false; result = action(); if (result == null) { return result; } cache.Insert(key, result, null, absoluteExpireTime, slidingExpireTime); } else { if (getOnly) { oldValueReturned = true; result = data; } else { oldValueReturned = false; result = action(); if (result == null) { return result; } cache.Insert(key, result, null, absoluteExpireTime, slidingExpireTime); } } } } else { if(getOnly) { oldValueReturned = true; result = data; } else { oldValueReturned = false; result = action(); if (result == null) { return result; } cache.Insert(key, result, null, absoluteExpireTime, slidingExpireTime); } } return result; }