在ASP.NET做正确锁紧 [英] Doing locking in ASP.NET correctly
问题描述
我有一个相当缓慢的搜索功能的ASP.NET网站,我想通过查询作为缓存键添加结果缓存一小时,以提高性能:
I have an ASP.NET site with a fairly slow search function, and I want to improve performance by adding the results to the cache for one hour using the query as the cache-key:
using System;
using System.Web;
using System.Web.Caching;
public class Search
{
private static object _cacheLock = new object();
public static string DoSearch(string query)
{
string results = "";
if (HttpContext.Current.Cache[query] == null)
{
lock (_cacheLock)
{
if (HttpContext.Current.Cache[query] == null)
{
results = GetResultsFromSlowDb(query);
HttpContext.Current.Cache.Add(query, results, null, DateTime.Now.AddHours(1), Cache.NoSlidingExpiration, CacheItemPriority.Normal, null);
}
else
{
results = HttpContext.Current.Cache[query].ToString();
}
}
}
else
{
results = HttpContext.Current.Cache[query].ToString();
}
return results;
}
private static string GetResultsFromSlowDb(string query)
{
return "Hello World!";
}
}
比方说,来访者做了搜索。缓存是空的,被设置了锁定,其结果是从数据库中请求。现在参观者B走来一个不同的搜索:不会参观者B具有由锁等到来访者的搜索已经完成?我真正想要的是对B立即调用数据库,因为结果将是不同的,该数据库可以处理多个请求 - 我只是不想重复昂贵的不必要的查询
Let’s say visitor A does a search. The cache is empty, the lock is set and the result is requested from the database. Now visitor B comes along with a different search: Won’t visitor B have to wait by the lock until visitor A’s search has completed? What I really wanted was for B to call the database immediately, because the results will be different and the database can handle multiple requests – I just don’t want to repeat expensive unnecessary queries.
什么是正确的做法对于这种情况?
What would be the correct approach for this scenario?
推荐答案
除非你是绝对肯定的是,这是已经没有多余的查询,然后我会避免完全锁定关键。 ASP.NET缓存本质上是线程安全的,所以唯一的缺点如下code是,你可能会暂时看到一些多余的查询比赛对方时,他们的关联的缓存条目到期:
Unless you're absolutely certain that it's critical to have no redundant queries then I would avoid locking altogether. The ASP.NET cache is inherently thread-safe, so the only drawback to the following code is that you might temporarily see a few redundant queries racing each other when their associated cache entry expires:
public static string DoSearch(string query)
{
var results = (string)HttpContext.Current.Cache[query];
if (results == null)
{
results = GetResultsFromSlowDb(query);
HttpContext.Current.Cache.Insert(query, results, null,
DateTime.Now.AddHours(1), Cache.NoSlidingExpiration);
}
return results;
}
如果你决定,你真的必须避免所有多余的查询,那么你可以使用一套更精细的锁具,每个查询一把锁:
If you decide that you really must avoid all redundant queries then you could use a set of more granular locks, one lock per query:
public static string DoSearch(string query)
{
var results = (string)HttpContext.Current.Cache[query];
if (results == null)
{
object miniLock = _miniLocks.GetOrAdd(query, k => new object());
lock (miniLock)
{
results = (string)HttpContext.Current.Cache[query];
if (results == null)
{
results = GetResultsFromSlowDb(query);
HttpContext.Current.Cache.Insert(query, results, null,
DateTime.Now.AddHours(1), Cache.NoSlidingExpiration);
}
object temp;
if (_miniLocks.TryGetValue(query, out temp) && (temp == miniLock))
_miniLocks.TryRemove(query);
}
}
return results;
}
private static readonly ConcurrentDictionary<string, object> _miniLocks =
new ConcurrentDictionary<string, object>();
这篇关于在ASP.NET做正确锁紧的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!