在字典的linq迭代期间可能导致堆栈溢出的原因是什么?

时间:2010-02-12 15:35:22

标签: c# linq multithreading dictionary stack-overflow

我有以下字典:

Dictionary<long, ChangeLogProcess> _changeLogProcesses = 
    new Dictionary<long, ChangeLogProcess>();

我有一个方法试图在特定状态的字典中获取下一个changelogprocess(如果没有特定状态的项,则返回null):

 var changeLogProcesses =
     from entry in _changeLogProcesses
     where (entry.Value.Status == status)
     select entry.Value;

 changeLogProcess = changeLogProcesses.FirstOrDefault<ChangeLogProcess>();

但是,在执行期间,它会在linq查询期间抛出堆栈溢出异常? 我做了很多测试,以确保列表中有项目等等,但问题仍然存在?

值得注意的是,此方法是在多线程环境中运行的服务的一部分。上面的linq查询(以及对它的所有访问,例如添加/删除到列表中的项目,或列表中项目的状态更改)都包含在ReaderWriterLockSlim写锁中。同样,我已经对它进行了广泛的调试,以确保在任何时候都不会有单个线程访问列表。

什么可能导致它堆栈溢出,因为它与某些可能的其他错误相关,例如在查询期间修改列表? (同样,我在任何时候都只有一个线程访问列表)

编辑:根据要求获取getter和setter代码:

 public ChangeLogProcessStatus Status 
 {
    get { return _status; }
    set
    {
        //more that one place can initiate a retry now, so retry count is handled in the property setter
        if (PreviousStatus <= ChangeLogProcessStatus.Waiting && value >= ChangeLogProcessStatus.RetryWaiting)
        {
            this.ChangeLog.Tries++;

            //If it's retry waiting, remove this last service machine from the 
            //list so it can try it again because it's not an error
            if (value == ChangeLogProcessStatus.RetryWaiting && _previousServiceMachineIds.Count > 0)
            {   
                _previousServiceMachineIds.RemoveAt(_previousServiceMachineIds.Count() - 1);
            }
        }

        PreviousStatus = _status;
        _status = value;

    }
}      

最后编辑 - 由于该代码中不存在该问题,我已删除了之前的示例。

事实证明它是在应用程序的不同部分,并且很难找到一段递归。巧合的是,在linq查询期间引发了堆栈溢出错误,结果被递归地调用了420000+次。

下面的所有答案都是有用的,并且在正确的路径上找到多线程应用程序中的问题,但是第一个答案肯定强调递归作为问题,结果是它(尽管它不是其中之一)属性访问者似乎很明显。)

再次感谢

由于

3 个答案:

答案 0 :(得分:5)

检查ChangeLogProcess类的属性以确保它不是自引用的。我认为,这是造成堆栈溢出异常的最可能原因。

例如:

 private ChangeLogStatus status;
 public ChangeLogStatus Status
 {
      get { return this.Status; }  // instead of this.status
      set { this.status = value }
 }

另一种可能的替代方案是在状态的相等性检查中。你有没有为ChangeLogStatus重写Equals()?检查那里以确保您没有任何自引用代码(或至少一种终止递归的方式)。

答案 1 :(得分:2)

我注意到一些集合在同时被两个线程触及时表现得非常糟糕。

您实际上允许多个线程同时触摸该集合。 RWLS允许多个线程在读取操作期间访问并锁定写入操作。因此,两个线程可以同时读取,即触摸该集合。

我的建议是将RWL​​S更改为一个简单的锁()并尝试重新编译堆栈溢出。

如果这可以解决您的问题,我建议您考虑转移到4.0以利用并发集合。或者,您可能想要构建自己的线程安全集合 cough reflector cough ,这样您就可以更好地控制这种情况。

答案 2 :(得分:1)

http://msdn.microsoft.com/en-us/library/xfhwa508.aspx

  

线程安全

     

公共静态(在Visual Basic中共享)   这种类型的成员是线程安全的。   不保证所有实例成员都是线程安全的。

     

字典&lt;(Of&lt;(TKey,TValue&gt;)&gt;)   可以支持多个读者   同时,只要   集合未被修改。 即便如此,   通过集合枚举是   本质上不是线程安全的   程序。在罕见的情况下   枚举与写作竞争   访问,集合必须   在整个过程中锁定   枚举。允许收集   由多个线程访问   阅读和写作,你必须   实现自己的同步。

强调我的。