2011-07-28 2 views
2

このエラーが断続的に発生する理由がわかりません。私は並列にデータバインドされているUserControlを持っています。コードは時間の90%で動作しますが、たびにデータバインドが失敗し、以下のエラーが発生します。Stack Emptyのために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クラス自体は配列ベースであり、配列はいっぱいになると大きな配列にコピーされます。アレイがコピーされているときに複数の値が同時に押された場合、コピー操作は2回実行され、すべてが失われます。これがPushDataBindingContextのコンテキストで発生すると、データ項目は実際にはスタックにプッシュされません。後でメソッドがスタックからプッシュしたアイテムをポップすると、スタックは空になり、例外がスローされます。

+0

drfに感謝、それは間違いなく正しい答えです。今スレッドの問題がなくてもこれを並行して実行する方法を理解するだけです... – cesara

1

例外はおそらくスレッドセーフの問題です。あなたが並行して、このようなコードを実行している複数のスレッドがある場合は特に、:

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

スタックはs.Counts.Pop()呼び出しの間に別のスレッドによって空にすることができます。スタックが空であるため、その後のs.Pop()の呼び出しは失敗します。

C#4の1つの代替(推奨)は、の代わりにSystem.Collections.Concurrent.ConcurrentStackを使用することです。このクラスには、TryPopというメソッドが含まれています。これは、スタックが空でない場合は、スタック内の最上位の項目を(outパラメータとして)返し、そうでない場合はfalseを返します。プロセスはアトミックなので、操作はスレッドセーフです。

第2のオプションはSyncRootプロパティを使用してスタックをロックすることである:これは、同時にスタックからアイテムを削除するから、複数のスレッドを防止する

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

+0

drf、返信ありがとうございます。唯一のことは、私は自分のDataBind()の内部でスタックを使用していないことです。 UserControls * base * DataBind()(Microsoftのコード)はスタックを使用します(このdatabindの内部構造はわかりません)。 – cesara

+0

申し訳ありませんが、私は誤解しました。私は問題が何かを見て、これを新しい答えに置き換えています。 – drf

関連する問題