索引超出范围异常

4
我有以下代码,但昨晚它突然抛出了很多异常:

引发了类型为'System.Web.HttpUnhandledException'的异常。 ---> System.IndexOutOfRangeException: 索引超出了数组界限。 at System.Collections.Generic.Dictionary`2.Insert(TKey key, TValue value, Boolean add)

我不明白这是如何可能的,我检查了空值和键是否可用。这是唯一使用lastTimeoutCheck的方法。
private static Dictionary<string, DateTime> lastTimeoutCheck;
private static readonly object CacheLock = new object();
private static void CheckTimeout(string groupName)
{
    if (lastTimeoutCheck == null)
    {
        lastTimeoutCheck = new Dictionary<string, DateTime>();
        return;
    }
    if (!lastTimeoutCheck.ContainsKey(groupName))
    {
        lastTimeoutCheck.Add(groupName, DateTime.UtcNow);
        return;
    }

    if (lastTimeoutCheck[groupName] <
        DateTime.UtcNow.AddMinutes(-GroupConfigSection.TimeOutCheckMinutes))
    {
        lock (CheckLock)
        {
            if (lastTimeoutCheck[groupName] <
                DateTime.UtcNow.AddMinutes(-GroupConfigSection.TimeOutCheckMinutes))
            {
                GroupHolder groupHolder =
                    (GroupHolder) System.Web.HttpContext.Current.Cache.Get(groupName);
                if (groupHolder != null)
                {
                    groupHolder.UpdateTime();
                }

                lastTimeoutCheck[groupName] = DateTime.UtcNow;
            }
        }
    }
}

2
你是否从多个线程中使用此方法?Dictionary<,>不是线程安全的。 - Jon Skeet
@Guillaume CheckLock的作用是确保在其他用户正在运行代码时不会执行该代码。 - BvdVen
@JLRishe 嗯,好的,那看起来是有道理的,我该如何防止这种情况发生呢? - BvdVen
@BvdVen 只是提供信息。为什么您要检查下面的语句两次?'if (lastTimeoutCheck[groupName] < DateTime.UtcNow.AddMinutes(-GroupConfigSection.TimeOutCheckMinutes))' - G J
1
代码在另一个用户正在运行代码时执行,因此您有多个线程! - Guillaume
显示剩余6条评论
1个回答

4
由于您的变量是static,并且错误指示它在Web服务器上运行,因此您很可能面临的问题是两个线程同时访问同一值,导致同时添加两个值。
解决方案取决于您的情况:
  1. 如果您不打算在会话之间共享它,则不要将字典设置为static。这并不能真正解决问题,只是使它更不可能发生;
  2. 使用线程安全的字典类型:ConcurrentDictionary

1
锁不会同时处理字典的更改吗? - BvdVen
@BvdVen:不行,因为并不是所有的访问都在锁定范围内执行。 - Jon Skeet
@BvdVen 是的,如果你锁定整个方法的内容,但是有效地使用锁需要洞察力和谨慎。你不能仅仅在担心并发访问的任何地方都加锁。那样可能会导致死锁、性能下降等问题。 - JLRishe
这个语句可能仍然会引起问题,因为它不在“lock”内部:lastTimeoutCheck.Add。它可能会导致字典重新调整大小,并且在此过程中可能会丢失数据。 - Patrick Hofman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接