2

http://msdn.microsoft.com/en-us/magazine/gg598924.aspx

WPFディスパッチャによって例外が伝播されないのはなぜですか。

タスク例外をUIスレッドに伝播させるにはどうすればよいですか。

以下のコードでは、タスクとその継続でスローされる例外をLogExceptionによって処理されるuiスレッドに戻す必要があります。もし私がその行に沿ってどこかに例外を投げ直す必要があるならば、それは私には問題ない。何でもいい。それ、どうやったら出来るの? 私の質問に似た質問をいくつか参照しましたが、自分のアプリに関連する質問は表示されません。

編集3:簡単な例を掲載しました

編集2:これを見なさい:http://msdn.microsoft.com/en-us/library/dd997415(v=vs.100).aspx

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        Loaded += new RoutedEventHandler(MainWindow_Loaded);
    }

    void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        FireAndForget();
        WaitOnTask();
    }

    private void FireAndForget()
    {
        Task t1 = Task.Factory.StartNew(() =>
        {
            Thread.Sleep(3000);
            throw new Exception("boo"); 
        });

        Task c1 = t1.ContinueWith((t) =>
            {
                // The app global exception handler will not catch this.

            }, TaskContinuationOptions.OnlyOnFaulted);

        //MessageBox.Show("Task is running");
    }


    private void WaitOnTask()
    {
        Task t1 = Task.Factory.StartNew(() =>
        {
            throw new Exception("boo");
        });

        try
        {
            t1.Wait();
        }
        catch (Exception ex)
        {
            // The app global exception handler will catch this:
            throw new Exception("Task", ex);
        }
    }
}



public partial class App : Application
{
    public App()
    {
        AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
        Application.Current.DispatcherUnhandledException += new System.Windows.Threading.DispatcherUnhandledExceptionEventHandler(Current_DispatcherUnhandledException);
        //System.Threading.Tasks.TaskScheduler.UnobservedTaskException += new EventHandler<System.Threading.Tasks.UnobservedTaskExceptionEventArgs>(TaskScheduler_UnobservedTaskException);
    }

    void TaskScheduler_UnobservedTaskException(object sender, System.Threading.Tasks.UnobservedTaskExceptionEventArgs e)
    {
        LogException(e.Exception);
    }

    void Current_DispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
    {
        LogException(e.Exception);
    }

    void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
        LogException(e.ExceptionObject as Exception);
    }

    private void LogException(Exception ex)
    {
        // log it
        string error = "This app has encountered an unexpected error .  The error message is:" + Environment.NewLine + ex.Message + Environment.NewLine;
        Exception tmp = ex.InnerException;

        while (tmp != null)
        {
            error += "Inner exception is: " + Environment.NewLine + tmp.Message + Environment.NewLine;
            tmp = tmp.InnerException;
        }

        error += "Please press OK to exit.";
        MessageBox.Show(error, "Error");
        Environment.Exit(-1);
    }
}


  • 使用しない理由はありますか。async/awaitの代わりにContinueWith? - Stephen Cleary
  • @StephenClearyはい、これは.net 4アプリです。 - Sam
  • 検討しましたかMicrosoft.Bcl.Async? - Stephen Cleary
  • @StephenCleary Stephen、ご提案をお寄せいただきありがとうございます - 私のアプリは非常に複雑なので、最後の手段として実行したいと思います。私が求めたことを達成するためのもっと直接的な方法はありますか? - Sam
  • それは私には思われるasyncだろう本当にアプリを単純化しますが、数分以内に回答が投稿されます。 - Stephen Cleary

5 답변


3

使うときStartNewまたはContinueWith例外が返されると、Task

マーシャリングの例外には2つの問題があります。

  1. Task.Exceptionあなたの例外をAggregateException
  2. 後で別のスレッドなどで例外をスローすると、元の呼び出しスタックは失われます。

最初の問題のために、何人かの人々はFlattenまたはHandle直接協力するメンバーAggregateException。次のものを扱うことによって、例外をアンラップすることを好みます。Task.Exception.InnerExceptionの代わりにTask.Exception

2番目の問題については、別の例外でそれをラップすることによってそれを回避する人もいますが、私は別の方法を取りました。 .NET 4.5が導入されましたExceptionDispatchInfoこれは正しいこれを行う方法。 .NET 4.0では、このようなことをハックすることができます。

public static Exception Rethrow(this Exception ex)
{
  typeof(Exception).GetMethod("PrepForRemoting",
      BindingFlags.NonPublic | BindingFlags.Instance)
      .Invoke(ex, new object[0]);
  throw ex;
}


  • Stephen、私はあなたのメソッドを実装し、私のコードを修正しました:t.Exception.Rethrow()しかしそれは機能しません。 - Sam
  • 「うまくいかない」とはどういう意味ですか。 - Stephen Cleary
  • 私の例外ハンドラは呼び出されません - Sam
  • 私があなたの言っていることの明確な描写を得ていません。最小限のリプロで質問を更新してください。 - Stephen Cleary
  • 変更が投稿されました。 - Sam

1

私はここに何かが足りないかどうかわからないが、あなたが使用する場合 ContinueWithの2番目のパラメーターとしてTaskScheduler.FromCurrentSynchronizationContext() それからそれはあなたのUXスレッドに元に戻されます。

あなたがもう少しサンプルを望むなら、私は実際にそれについてブログ投稿を書きました。http://www.briankeating.net/post/Why-I-love-the-Task-library

KR、 ブライアン。


  • コードがインターフェイススレッドで整列化されることは事実ですが、継続的にスローされるすべての例外は異なる方法で処理されます - それらは次のようにして報告されます。TaskScheduler.UnobservedTaskExceptionの代わりにDispatcher.UnhandledException - ghord

0

質問への答えはここにあります:http://blogs.msdn.com/b/pfxteam/archive/2009/05/31/9674669.aspx

基本的に2つのシナリオがあります:あなたがタスクを待つことができる状況とあなたが発射することができない状況、すなわち忘れること。 タスクを待つことができる状況では、質問に示されているようにtryブロックでそれをラップし、エラーを再スローしてください。グローバルアプリハンドラがそれをキャッチします。

あなたが仕事を待つことができない施設では、手動であなたのロガーを呼ばなければなりません。エラーをキャッチするアプリケーションレベルのハンドラはありません。 TaskScheduler.UnobservedTaskExceptionが発生する可能性がありますが、そのイベントは非常に状況的で脆弱であり、適切な選択肢ではありません。


0

コード内で例外を伝播させるには、Waitすべてのタスクに。次のように変更した場合FireAndForgetメソッドException入れ子にTask呼び出しスレッドに伝搬されます。

    private void FireAndForget()
    {
        var tasks = new Task[2];
        tasks[0] = Task.Factory.StartNew(() =>
        {
            Thread.Sleep(3000);
            throw new Exception("boo");
        });

        tasks[1] = tasks[0].ContinueWith((t) =>
        {
            throw new Exception("nested boo", tasks[0].Exception);

        }, TaskContinuationOptions.OnlyOnFaulted);

        try
        {
            Task.WaitAll(tasks);
        }
        catch (AggregateException ex)
        {
            throw new Exception("Task", ex);
        }
    }

もちろん、これはもはや「消し忘れ」方法ではありません。タスクを待つのが望ましくない場合は、継続的にログファイルに書き込む必要があります。


  • そう、またはtask [0]の中にtry / catchブロックを追加する - Sam

-1

あなたはできるawaitタスクコードから例外を受け取るためのタスクの完了

try{
  await Task.Factory.StartNew(() => throw Exception("hello"));       
}catch{
  // will get exception here
}


  • 私は。私は。ネット4を使用して待っていることはできません。 - Sam

リンクされた質問


関連する質問

最近の質問