2011-07-28 59 views
2

我不知道爲什麼這個錯誤是間歇性發生的。我有一個並行數據綁定的UserControl。該代碼在90%的時間內工作,但每隔一段時間,數據綁定將失敗並將收到以下錯誤。由於堆棧爲空而導致Parallel.Invoke中的DataBind()失敗。錯誤

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) 

任何人都知道爲什麼會發生這種情況,以及如何避免?

回答

2

這是一個併發問題。 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的上下文中時,數據項永遠不會被推送到堆棧 - 當方法稍後試圖彈出它從堆棧推出的項時,堆棧將爲空,並拋出異常。

+0

感謝drf,這絕對是正確的答案。現在只想知道如何在沒有線程問題的情況下並行執行此操作。 – cesara

1

異常可能是線程安全問題。特別是,如果你在並行運行這樣的代碼多線程:

// s is a instance of Stack 
if (s.Count > 0) 
    s.Pop() 

堆棧可以通過s.Counts.Pop()通話之間另一個線程清空。隨後調用s.Pop()失敗,因爲堆棧爲空。

C#4中的一種(推薦)替代方法是使用System.Collections.Concurrent.ConcurrentStack代替Stack。該類包括方法TryPop,如果堆棧不爲空,它將返回(作爲out參數)棧中的頂層項目,否則返回false。由於該過程是原子操作,因此該操作是線程安全的。

第二選項是使用SyncRoot屬性鎖定堆棧:

lock(s.SyncRoot) 
{ 
    if (s.Count > 0) 
     s.Pop(); 
} 

這將防止多個線程從棧同時刪除項目。

+0

drf,謝謝你的回覆。唯一的是我沒有在我的DataBind()中使用堆棧。 UserControls * base * DataBind()(微軟的代碼)使用棧(我不知道這個數據綁定的內部)。 – cesara

+0

對不起,我誤解了。我看到問題出在哪裏,並用新的答案替換。 – drf

相關問題