Jak*_*ade 7 .net c# asp.net caching locking
我有一个具有相当慢的搜索功能的ASP.NET站点,我想通过使用查询作为缓存键将结果添加到缓存一小时来提高性能:
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!";
}
}
Run Code Online (Sandbox Code Playgroud)
假设访问者A进行搜索.缓存为空,设置锁定并从数据库请求结果.现在访问者B进行了不同的搜索:访问者B的搜索完成之前,访问者B是否必须等待锁定?我真正想要的是B立即调用数据库,因为结果会有所不同,数据库可以处理多个请求 - 我只是不想重复昂贵的不必要的查询.
这种情况的正确方法是什么?
Luk*_*keH 25
除非你绝对确定没有冗余查询是至关重要的,否则我会完全避免锁定.ASP.NET缓存本质上是线程安全的,因此以下代码的唯一缺点是,当相关的缓存条目到期时,您可能会暂时看到一些冗余查询相互竞争:
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;
}
Run Code Online (Sandbox Code Playgroud)
如果您确定必须避免所有冗余查询,那么您可以使用一组更精细的锁,每个查询一个锁:
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>();
Run Code Online (Sandbox Code Playgroud)
您的代码有潜在的竞争条件:
if (HttpContext.Current.Cache[query] == null)
{
...
}
else
{
// When you get here, another thread may have removed the item from the cache
// so this may still return null.
results = HttpContext.Current.Cache[query].ToString();
}
Run Code Online (Sandbox Code Playgroud)
一般情况下我不会使用锁定,并且会按照以下方式执行以避免竞争条件:
results = HttpContext.Current.Cache[query];
if (HttpContext.Current.Cache[query] == null)
{
results = GetResultsFromSomewhere();
HttpContext.Current.Cache.Add(query, results,...);
}
return results;
Run Code Online (Sandbox Code Playgroud)
在上述情况下,如果多个线程在大约同一时间检测到高速缓存未命中,则它们可能会尝试加载数据.在实践中,这可能很少见,并且在大多数情况下并不重要,因为它们加载的数据将是等效的.
但是如果你想使用锁来防止它,你可以这样做:
results = HttpContext.Current.Cache[query];
if (results == null)
{
lock(someLock)
{
results = HttpContext.Current.Cache[query];
if (results == null)
{
results = GetResultsFromSomewhere();
HttpContext.Current.Cache.Add(query, results,...);
}
}
}
return results;
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
11686 次 |
| 最近记录: |