我看到一个随机异常“集合被修改;枚举可能无法执行” - InvalidOperationException。
这个例外指向下面代码片段中的foreach行,我知道在枚举时修改了一个集合。
然而在我的场景中,我没有看到它真正发生的可能性 - 除非私有成员不是线程安全的......我可能错了,但这需要帮助才能理解和弄清楚
以下是我的代码的外观
我的代码背后有一个像
这样的私有集合private Dictionary<string, string> _someDictionary = SomeConstantClass.ConstantValue;
在页面预呈现完成事件中,我正在枚举字典
protected override void OnPagePreRenderComplete(object sender, EventArgs e){
_someDictionary["AnotherKey"] = "Another value";
foreach(var dataValuePair in _SomeDictionary){
//Do some operation
}
}
我还有一个可以修改此集合的公共属性,但它在ascx文件中设置为
<tc: UserControlA runat="server" id="abc" CustomProperty="true" />
这是它的实现,
public bool CustomProperty{
set{
if (value)
_someDictionary["CustomProperty"] = "Custom Value";
}
}
它肯定修改了我的成员变量集合 - 但是根据我的理解,这个属性应该在Control Init本身中触发并完成。
所以,我仍然没有看到在预渲染完成事件期间修改集合的场景。
知道什么可能导致异常发生吗?
其他说明:页面肯定有很多更新面板,虽然这个特定的用户控件没有做任何花哨的事情,甚至没有回发场景。 从日志中我看到问题发生在对页面的HTTP GET请求中。
此外:建议我重现这一点(如果有的话)。
对于有兴趣了解SomeConstantClass.ConstantValue的朋友,这里是
class SomeConstantClass{
public static Dictionary<string, string> ConstantValue = new Dictionary<string, string> {
{"ABCD", "EFGH"},
{"HIJK", "LMNO"}
};
}
答案 0 :(得分:7)
如果从SomeConstantClass.ConstantValue
返回相同的实例,那么多个页面将使私有成员变量指向同一个对象。这将导致对象在一页的init上进行更改,同时在另一页的OnPagePreRenderComplete
上进行迭代。
确保在每次访问SomeConstantClass.ConstantValue
时返回字典的新实例。例如:
public static Dictionary<string, string> ConstantValue
{
get
{
return new Dictionary<string, string>
{
{"ABCD", "EFGH"},
{"HIJK", "LMNO"}
};
}
}
这样每个页面都有自己的字典对象可供使用。这是快速解决方案,您可以重构逻辑,这样您就不必为每个页面创建新的字典。
基本上,私有成员变量只有线程安全,如果它们引用该页面专用的对象,并且没有其他人知道该对象或该对象本身被设计为线程安全的。通过私有成员封装对非线程安全的静态对象的访问不会使其成为线程安全的。
答案 1 :(得分:2)
只要你知道你没有跨越请求共享字典(或者你故意这样做),你可以跳过尝试找出原因,并且只使用线程安全的Concurrent Dictionary。仅仅因为字典是私有的并不能使它成为线程安全的。