我不确定为什么这个错误会间歇性地发生。我有一个并行绑定数据的UserControl。代码在90%的时间内都能正常工作,但有时数据库绑定会失败,并收到下面的错误。
at System.Collections.Stack.Pop()
at System.Web.UI.Control.DataBind(Boolean raiseOnDataBinding)
at System.Web.UI.WebControls.Repeater.CreateItem(Int32 itemIndex, ListItemType itemType, Boolean dataBind, Object dataItem)
at System.Web.UI.WebControls.Repeater.CreateControlHierarchy(Boolean useDataSource)
at System.Web.UI.WebControls.Repeater.OnDataBinding(EventArgs e)有人知道为什么会发生这种情况,以及如何避免吗?
发布于 2011-07-28 16:38:14
这是一个并发问题。web控件are not guaranteed to be type-safe上的实例方法。因此,不应该在多个线程上同时调用DataBind (和其他实例方法)。
至于发生这种情况的原因:Control class implementation包含一个内部Page实例;该实例有一个用于数据绑定的内部堆栈。
protected virtual void DataBind(bool raiseOnDataBinding) {
bool inDataBind = false;
if (foundDataItem && (Page != null)) {
Page.PushDataBindingContext(dataItem);
inDataBind = true;
}
try{
//...
} finally {
if (inDataBind) {
Page.PopDataBindingContext();
}
}
}通常,每次推送都会伴随着后来的弹出,以确保堆栈永远不会为空。但是,Stack类本身是基于数组的,数组在填充时被复制到更大的数组中。如果在复制数组时同时推送多个值,则在某些情况下,复制操作可能会执行两次,所有操作都会丢失。当在PushDataBindingContext上下文中发生这种情况时,数据项实际上永远不会被推送到堆栈--当该方法稍后尝试从堆栈中弹出它推送的项时,堆栈是空的,并且会抛出异常。
发布于 2011-07-28 09:04:49
异常很可能是线程安全问题。特别是,如果你有多个线程并行运行这样的代码:
// s is a instance of Stack
if (s.Count > 0)
s.Pop()堆栈可以由s.Count和s.Pop()调用之间的另一个线程清空。由于堆栈为空,因此对s.Pop()的后续调用失败。
在C# 4中,一种(推荐的)替代方案是使用System.Collections.Concurrent.ConcurrentStack来代替Stack。这个类包括方法TryPop,如果堆栈不为空,它将返回堆栈中的顶部项(作为输出参数),否则返回false。因为进程是原子的,所以操作是线程安全的。
第二种选择是使用SyncRoot属性锁定堆栈:
lock(s.SyncRoot)
{
if (s.Count > 0)
s.Pop();
}这将防止多个线程同时从堆栈中删除项。
https://stackoverflow.com/questions/6852857
复制相似问题