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);
}
}
使うときStartNew
またはContinueWith
例外が返されると、Task
。
マーシャリングの例外には2つの問題があります。
Task.Exception
あなたの例外をAggregateException
。最初の問題のために、何人かの人々は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;
}
私はここに何かが足りないかどうかわからないが、あなたが使用する場合 ContinueWithの2番目のパラメーターとしてTaskScheduler.FromCurrentSynchronizationContext() それからそれはあなたのUXスレッドに元に戻されます。
あなたがもう少しサンプルを望むなら、私は実際にそれについてブログ投稿を書きました。http://www.briankeating.net/post/Why-I-love-the-Task-library
KR、 ブライアン。
TaskScheduler.UnobservedTaskException
の代わりにDispatcher.UnhandledException
- ghord
質問への答えはここにあります:http://blogs.msdn.com/b/pfxteam/archive/2009/05/31/9674669.aspx
基本的に2つのシナリオがあります:あなたがタスクを待つことができる状況とあなたが発射することができない状況、すなわち忘れること。 タスクを待つことができる状況では、質問に示されているようにtryブロックでそれをラップし、エラーを再スローしてください。グローバルアプリハンドラがそれをキャッチします。
あなたが仕事を待つことができない施設では、手動であなたのロガーを呼ばなければなりません。エラーをキャッチするアプリケーションレベルのハンドラはありません。 TaskScheduler.UnobservedTaskExceptionが発生する可能性がありますが、そのイベントは非常に状況的で脆弱であり、適切な選択肢ではありません。
コード内で例外を伝播させるには、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);
}
}
もちろん、これはもはや「消し忘れ」方法ではありません。タスクを待つのが望ましくない場合は、継続的にログファイルに書き込む必要があります。
あなたはできるawait
タスクコードから例外を受け取るためのタスクの完了
try{
await Task.Factory.StartNew(() => throw Exception("hello"));
}catch{
// will get exception here
}
async
/await
の代わりにContinueWith
? - Stephen ClearyMicrosoft.Bcl.Async
? - Stephen Clearyasync
だろう本当にアプリを単純化しますが、数分以内に回答が投稿されます。 - Stephen Cleary