http://msdn.microsoft.com/en-us/magazine/gg598924.aspx
Why exceptions are not propagated by WPF Dispatcher.Invoke?
How can I allow Task exceptions to propagate back to the UI thread?
在下面的代码中,我需要将在任务及其延续中抛出的异常传播回ui线程,在那里它们将由LogException处理。如果我需要在线路的某个地方重新抛出异常,那对我很好。无论什么有效。我怎么做? 我引用了一些类似于我的问题,但我没有看到与我的应用相关的答案 编辑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);
}
}
答案 0 :(得分:3)
当您使用StartNew
或ContinueWith
时,会对返回的Task
发出任何例外情况。
编组异常有两个问题:
Task.Exception
将您的例外包装在AggregateException
。对于第一个问题,有些人使用Flatten
或Handle
成员直接与AggregateException
合作。我更喜欢通过处理Task.Exception.InnerException
代替Task.Exception
来解开异常。
对于第二个问题,有些人通过将其包装在另一个例外来解决它,但我采取了另一种方法。 .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;
}
答案 1 :(得分:1)
我不确定我是否在这里遗漏了一些东西,但如果你使用的话 TaskScheduler.FromCurrentSynchronizationContext()作为ContinueWith的第二个参数 然后它将被整理回你的UX线程。
如果你想要更多的样本,我实际上写了一篇关于它的博客文章。 http://www.briankeating.net/post/Why-I-love-the-Task-library
氪, 布赖恩。
答案 2 :(得分:0)
问题的答案可在此处找到: http://blogs.msdn.com/b/pfxteam/archive/2009/05/31/9674669.aspx
基本上有两种情况:你可以等待任务的情况以及你不能发射和忘记的情况。 在您可以等待任务的情况下,将其包装在问题块中,如问题所示,并重新抛出错误。全局应用程序处理程序将捕获它。
在您无法等待任务的情况下,您必须手动调用记录器。没有应用程序级别处理程序可以捕获错误。有可能会触发TaskScheduler.UnobservedTaskException,但该事件是恕我直言,非常间谍和脆弱,并不是一个好的选择。
答案 3 :(得分: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);
}
}
当然,这不再是一种“昙花一现”的方法。如果不希望等待任务,则需要从延续中写入日志文件。
答案 4 :(得分:-1)
您可以await
完成任务以从任务代码中接收异常。
try{
await Task.Factory.StartNew(() => throw Exception("hello"));
}catch{
// will get exception here
}