3

WPF で未処理の例外を使用して、「興味深い」動作が発生しているようです。

簡単に言えば、ディスパッチャ スレッドで短期間に同じ例外が 2 回スローされると、ディスパッチャの未処理の例外ハンドラがバイパスされ、アプリケーションがダウンします。

ステップを再現

  • 新しい WPF アプリケーションを作成する
  • DispatcherUnhandledException ハンドラーを作成し、e.Handled を true に設定して、例外を表示するメッセージ ボックスを開きます。
  • AppDomain.CurrentDomain.UnhandledException ハンドラーを作成し、ここからもメッセージ ボックスを表示します (ここで例外を処理できないことに注意してください。ここで例外を処理すると、アプリが停止しようとしていることを意味します)。
  • ここでボタンを追加し、クリック ハンドラーに以下を追加します。

    SynchronizationContext.Current.Post(s => { throw new Exception(); }, null);
    SynchronizationContext.Current.Post(s => { throw new Exception(); }, null);
    

DispatcherUnhandledException ハンドラーが 2 回発生し、両方の例外が処理され、すべてが正常であることがわかります。

ただし、上記のコードを次のように変更します。

    var ex = new Exception();
    SynchronizationContext.Current.Post(s => { throw ex; }, null);
    SynchronizationContext.Current.Post(s => { throw ex; }, null);

そして、AppDomain.CurrentDomain.UnhandledException ハンドラーが発生し、アプリケーションが典型的なウィンドウの「デバッグしますか?」ダイアログでクラッシュすることがわかります。

追加情報

この例は、問題を単純化するために考案されたものに見えるかもしれません。ただし、このシナリオは、ストリームに 2 つの RX サブスクライバーがあり、エラーが発生した場合に発生する可能性があります。この場合、両方のサブスクライバーが同じ例外を発生させ、上記と同じ動作を引き起こします。たとえば、ボタン クリック ハンドラーの次の RX コードでも問題が再現されます (これも不自然ですが、同等の状況になる可能性があります)。

        var o = Observable.Start(() => { throw new Exception(); }).Publish();
        o.ObserveOnDispatcher().Subscribe(_ => { });
        o.ObserveOnDispatcher().Subscribe(_ => { });
        o.Connect();
4

1 に答える 1

3

これはディスパッチャの動作のようです。以前に例外を「認識」したかどうかを確認し(例外のデータにタグを追加することにより)、そうでない場合にのみ処理します。

 private bool ExceptionFilter(Exception e)
    {
        // see whether this dispatcher has already seen the exception.
        // This can happen when the dispatcher is re-entered via
        // PushFrame (or similar).
        if (!e.Data.Contains(ExceptionDataKey))
        {
            // first time we've seen this exception - add data to the exception
            e.Data.Add(ExceptionDataKey, null);
        }
        else
        {
            // we've seen this exception before - don't catch it
            return false;
        }
        ....

http://reflector.webtropy.com/default.aspx/4@0/4@0/untmp/DEVDIV_TFS/Dev10/Releases/RTMRel/wpf/src/Base/System/Windows/Threading/Dispatcher@cs/1407647/ Dispatcher @ cs

これが意味するのは、例外をキャッチして再ラップし(つまり、ディスパッチャが同じものとして認識しないように新しい例外オブジェクトを作成する)、アプリケーションがダウンしないようにする必要があるということです。

于 2013-01-21T10:02:09.200 に答える