下面有一些我们用来提高性能的代码。它工作正常,但是每隔几天我们就会收到大量异常(如下)。它与音量无关,但是是随机的。

注释:///如果需要,在线程锁定它然后缓存结果时执行锁定的代码以产生结果。

第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'被抛出。 ->
  System.ArgumentNullException:值不能为null。参数名称:
  System.Web.Caching.CacheInternal.DoGet(Boolean isPublic,String
  键,CacheGetOptions getOptions)在PROJECT.LockedCaching.b__8(String
  k)在PROJECT \ LockedCaching.cs:第64行
  System.Collections.Generic.List 1.RemoveAll(Predicate 1个匹配项)
  PROJECT.LockedCaching.CleanUpOldKeys()在
  PROJECT \ LockedCaching.cs:第64行位于
  PROJECTLockedCaching.Request(String key,DateTime expiration,
  PROJECT \ LockedCaching.cs:第58行的RequestDataOperation数据)
  FeatureWithFlags1.DataBind()在
  System.Web.Util.CalliHelper.EventArgFunctionCaller(IntPtr fp,Object
  o,对象t,EventArgs e)位于System.Web.UI.Control.LoadRecursive()处
  System.Web.UI.Control.LoadRecursive()在
  System.Web.UI.Control.LoadRecursive()在
  System.Web.UI.Control.LoadRecursive()在
  System.Web.UI.Control.LoadRecursive()在
  System.Web.UI.Control.LoadRecursive()在
  System.Web.UI.Page.ProcessRequestMain(布尔
  includeStagesBeforeAsyncPoint,布尔值includeStagesAfterAsyncPoint)
  在System.Web.UI.Page.HandleError(Exception e)在
  System.Web.UI.Page.ProcessRequestMain(布尔
  includeStagesBeforeAsyncPoint,布尔值includeStagesAfterAsyncPoint)
  在System.Web.UI.Page.ProcessRequest(布尔
  includeStagesBeforeAsyncPoint,布尔值includeStagesAfterAsyncPoint)
  在System.Web.UI.Page.ProcessRequest()处
  System.Web.UI.Page.ProcessRequest(HttpContext上下文)位于
  System.Web.HttpApplication.CallHandlerExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
  在System.Web.HttpApplication.ExecuteStep(IExecutionStep步骤,
  布尔值(已同步完成)


Web控件的使用位置-此Web控件从Web服务请求位置列表。我们几乎在调用Web服务的所有地方都使用了这个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 => k == key))”返回的是异常而不是null。使用并发字典将更加安全,并且希望不会引起任何锁定问题。

修改后的代码:

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>();

}

最佳答案

您的代码在集合上具有竞争条件。您同时写入。这会产生各种影响。

_keys.Add(key);
...
_keys.RemoveAll(k => HttpContext.Current.Cache[k] == null);


还有其他种族。您可能应该修改扩展置于全局锁定范围内的代码量。注意不要通过使用该全局锁破坏太多的并发。

也许您可以切换到ConcurrentDictionary<string, Lazy<CacheValue>>。这是一种像您一样的缓存的规范模式。它不会遭受缓存标记。

穿线时要小心。在这种情况下,很容易引入微妙的比赛。

07-24 09:50
查看更多