我们使用以下代码来提高性能。它工作正常,但每隔几天我们开始收到大量的例外(下面)。它与音量无关,但它是随机的。
注释:///执行锁定的代码,以便在必要时生成结果,同时将其锁定,然后缓存结果。
第45行是:lock(_keys.First(k => k == key))
任何想法?
代码:
public class LockedCaching
{
private static List<string> _keys = new List<string>();
public class Result
{
public object Value { get; set; }
public bool ExecutedDataOperation { get; set; }
}
/// <summary>
/// Performs the locked code to produce the result if necessary while thread locking it and then caching the result.
/// </summary>
/// <param name="key"></param>
/// <param name="expiration"></param>
/// <param name="data"></param>
/// <returns></returns>
public static Result Request(string key, DateTime expiration, RequestDataOperation data)
{
if (key == null)
{
return new Result { Value = data(), ExecutedDataOperation = true };
}
//Does the key have an instance for locking yet (in our _keys list)?
bool addedKey = false;
bool executedDataOperation = false;
if (!_keys.Exists(s => s == key))
{
_keys.Add(key);
addedKey = true;
}
object ret = HttpContext.Current.Cache[key];
if (ret == null)
{
lock (_keys.First(k => k == key))
{
ret = HttpContext.Current.Cache[key];
if (ret == null)
{
ret = data();
executedDataOperation = true;
if(ret != null)
HttpContext.Current.Cache.Insert(key, ret, null, expiration, new TimeSpan(0));
}
}
}
if (addedKey)
CleanUpOldKeys();
return new Result { Value = ret, ExecutedDataOperation = executedDataOperation };
}
private static void CleanUpOldKeys()
{
_keys.RemoveAll(k => HttpContext.Current.Cache[k] == null);
}
}
例外:
异常:System.Web.HttpUnhandledException(0x80004005):异常 抛出了'System.Web.HttpUnhandledException'类型。 ---&GT; System.ArgumentNullException:值不能为null。参数名称: System.Web.Caching.CacheInternal.DoGet上的键(Boolean isPublic,String 密钥,CacheGetOptions getOptions)在PROJECT.LockedCaching.b__8(字符串 k)在PROJECT \ LockedCaching.cs中:第64行at System.Collections.Generic.List
1.RemoveAll(Predicate
1匹配)at PROJECT.LockedCaching.CleanUpOldKeys()in PROJECT \ LockedCaching.cs:第64行 PROJECTLockedCaching.Request(String key,DateTime expiration, PROJECT \ LockedCaching.cs中的RequestDataOperation数据):第58行 FeaturesWithFlags1.DataBind()at System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp,Object o,对象t,EventArgs e)在System.Web.UI.Control.LoadRecursive()at System.Web.UI.Control.LoadRecursive()at System.Web.UI.Control.LoadRecursive()at System.Web.UI.Control.LoadRecursive()at System.Web.UI.Control.LoadRecursive()at System.Web.UI.Control.LoadRecursive()at System.Web.UI.Page.ProcessRequestMain(布尔 includeStagesBeforeAsyncPoint,Boolean includeStagesAfterAsyncPoint) 在System.Web.UI.Page.HandleError(Exception e)at System.Web.UI.Page.ProcessRequestMain(布尔 includeStagesBeforeAsyncPoint,Boolean includeStagesAfterAsyncPoint) 在System.Web.UI.Page.ProcessRequest(布尔值 includeStagesBeforeAsyncPoint,Boolean includeStagesAfterAsyncPoint) 在System.Web.UI.Page.ProcessRequest()处 System.Web.UI.Page.ProcessRequest(HttpContext context)at System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() 在System.Web.HttpApplication.ExecuteStep(IExecutionStep步骤, 布尔和放大器; completedSynchronously)
使用它的Web控件 - 此Web控件从Web服务请求位置列表。我们几乎在所谓的webservice上使用这个lockedcache请求。:
public override void DataBind()
{
try
{
string cacheKey = "GetSites|";
mt_site_config[] sites = (mt_site_config[])LockedCaching.Request(cacheKey, DateTime.UtcNow.AddMinutes(10),
() =>
{
WebServiceClient service = new WebServiceClient();
sites = service.GetSites();
service.Close();
return sites;
}).Value;
ddlLocation.Items.Clear();
ddlLocation.Items.Add(new ListItem("Please Select"));
ddlLocation.Items.Add(new ListItem("Administration"));
ddlLocation.Items.AddRange
(
sites.Select
(
s => new ListItem(s.site_name + " " + s.site_location, s.th_code.ToString())
).ToArray()
);
}
catch (Exception ex) {
Logger.Error("ContactUs Control Exception: Exp" + Environment.NewLine + ex.Message);
}
base.DataBind();
}
感谢您的评论。 ConcurrentDictionary是要走的路。我们收到错误的原因是因为linq代码“lock(_keys.First(k =&gt; k == key))”返回异常而不是null。使用concurrentdictionary将更加安全,并且希望不会导致任何锁定问题。
修改后的代码:
public class LockedCaching
{
public class Result
{
public object Value { get; set; }
public bool ExecutedDataOperation { get; set; }
}
public static Result Request(string key, DateTime expiration, RequestDataOperation data)
{
if (key == null)
{
return new Result { Value = data(), ExecutedDataOperation = true };
}
object results = HttpContext.Current.Cache[key];
bool executedDataOperation = false;
if (results == null)
{
object miniLock = _miniLocks.GetOrAdd(key, k => new object());
lock (miniLock)
{
results = HttpContext.Current.Cache[key];
if (results == null)
{
results = data();
executedDataOperation = true;
if (results != null)
HttpContext.Current.Cache.Insert(key, results, null, expiration, new TimeSpan(0));
object temp;
object tempResults;
if (_miniLocks.TryGetValue(key, out temp) && (temp == miniLock))
_miniLocks.TryRemove(key, out tempResults);
}
}
}
return new Result { Value = results, ExecutedDataOperation = executedDataOperation };
}
private static readonly ConcurrentDictionary<string, object> _miniLocks =
new ConcurrentDictionary<string, object>();
}
答案 0 :(得分:3)
您的代码在集合中存在竞争条件。你同时写信给它。这可以产生各种各样的效果。
_keys.Add(key);
...
_keys.RemoveAll(k => HttpContext.Current.Cache[k] == null);
还有其他种族。您可能应该修改扩展您在全局锁定下的代码量。注意不要使用全局锁来破坏过多的并发。
也许你可以切换到ConcurrentDictionary<string, Lazy<CacheValue>>
。这是与您的缓存一样的缓存的规范模式。它不会受到缓存加盖的影响。
小心线程。在这种情况下很容易引入微妙的比赛。
答案 1 :(得分:0)
您看到的异常表示_keys
中包含空元素。从您的代码段开始,这不应该发生。因此,我们看不到的其他代码是添加空值,或者您遇到线程安全问题。因为你几乎肯定有线程安全漏洞(参见问题评论),我会开始寻找它。