2012-03-27 9 views
3

どうしてこれが動作しないのですか?デリゲート内の静的オブジェクトのロックが機能していない

private static object Lock_HandleError = new object(); 
public static void HandleError(Exception ex) 
{ 
    lock(Lock_HandleError) 
    { 
     //IF the UI is processing a visual tree event (such as IsVisibleChanged), it throws an exception when showing a MessageBox as described here: http://social.msdn.microsoft.com/forums/en-US/wpf/thread/44962927-006e-4629-9aa3-100357861442 
     //The solution is to dispatch and queue the MessageBox. We must use BeginInvoke because dispatcher processing is suspended in such cases. 
     Dispatcher.CurrentDispatcher.BeginInvoke((Action)delegate() 
     { 
      lock(Lock_HandleError) 
      { 
       Dispatcher.CurrentDispatcher.BeginInvoke((Action)delegate(){ 
        HandleError(new Exception("testing purposes only")); 
       }, DispatcherPriority.Background); 

       MessageBox.Show(ex.Message, "Application Error", MessageBoxButton.OK, MessageBoxImage.Error); 
       //This point is not reached until the user clicks "OK" 
      } 
     }, DispatcherPriority.Background); 
    } 
} 

public void main() 
{ 
    HandleError(new Exception("The first error")); 
} 

上記のコードの予想される動作は1件のエラーメッセージが一度に表示されることで、ユーザが「OK」をクリックすると、Lock_HandleErrorオブジェクトに派遣スレッドから解放されます、次のHandleErrorへの呼び出しを進めることができますが、私が得ているのは、 "OK"を打つことなく、エラーメッセージの無限カスケードです。

なぜこのロックが機能しないのですか?

各ロックステートメントの出入り口にブレークポイントを設定することで、デリゲートがlock()を呼び出していることがわかり、再度「HandleError」への新しい呼び出しをディスパッチして、MessageBoxでユーザー入力を待つことを一時停止します。

一方、別のスレッドでは、HandleErrorの呼び出しが実行されますが、MessageBoxデリゲートがロックを明示的に配置していて、まだロックされていないにもかかわらず、それをリリースした。

+0

私はマルチスレッドで傷ついていませんが、 'BeginInvoke'メソッドで' lock'を削除できないのでしょうか? – Richard

+0

ロックを解除しても何も成立しません。ロックはそのままであるように動作します。 UIスレッドは、ユーザーがメッセージボックスで「OK」をクリックするのを待機している間、アプリケーションの他の領域からのHandleError()への追加呼び出しを処理します。停止して待機する必要があります。 – Alain

+0

私はここでセマフォを使用して問題を解決しようとしました:http://stackoverflow.com/questions/9894750/how-can-i-get-the-ui-thread-to-wait-on-a-semaphore-but -process-additional-dispa – Alain

答えて

6

二つの部分の答え:

  1. ロックが再入していることを理解します。スレッドがすでにオブジェクトにロックを保持している場合、そのスレッドはブロックせずに同じロックを何度も繰り返すことができます。

  2. 最初のMessageBoxが起動している間も、UIスレッドはまだメッセージをポンピングしていますので、HandleErrorへの後続の(再帰的な)呼び出しはUIスレッドで処理されます(既にロックを保持しているため、 )。

+0

これまでのところ、これは最も完全な説明です。私はMessageBoxが実行していたスレッドをブロックすることを期待していたので( '2'によると)、私は期待していましたどのスレッドがそれを所有しているかにかかわらず、セマフォのようにブロックするようにロックします( '1'によるとそうではありません)。私の質問は、私の問題の解決策を明示的に要求していませんが、なぜ私の問題が解決しないのかは分かりませんが、この種の問題に対する簡単な修正があるかどうかはまだ分かりません。 – Alain

6

なぜこのロックが機能しないのですか?

スレッドは、すでに所有しているロック文を入力することができます。本質的に、ロックはそれ自身のスレッドをブロックしません。

このように、元のスレッドはロックを取り、ディスパッチャのキューにメッセージを追加することができます。それは必要な数だけ追加することができます。

Dispatcherは、処理時に最初のメッセージを取得してから、HandleErrorを呼び出します。これはディスパッチャスレッドで実行されているため、外部ロックと内部ロックを入力してHandleErrorを再度呼び出すことができ、エンドレスループでキューに新しいメッセージを再帰的に追加することができます。

+0

説明をありがとう。明らかにlock()は私が使用したいものではありません。どのように私は意図したものを達成することができますか? – Alain

+0

@AlainなぜBeginInvokeを使って再帰的に自分自身を呼び出していますか?それは問題になるでしょう。あなたがした行動をあなたに与えたものが、あなたを事実上死んでしまうでしょう。 –

+0

実際に何が起こるのかをシミュレートするのは、多くの異なるスレッドが同時にエラーを経験し、それらはすべてUIスレッド上で 'HandleError()'への呼び出しを送出するが、次のエラーを処理する前にMessageBoxを閉じるのを待つのではなくアプリケーションはすべてのエラーを同時にカスケードします。繰り返しエラーが発生した場合、このロックが機能しないため、メッセージボックスの無限カスケードを検出して処理できなくなります。 – Alain