C# 用TPL处理异常
使用TPL/Tasks,我可以使用内部try/catch语句执行异常处理:C# 用TPL处理异常,c#,task-parallel-library,C#,Task Parallel Library,使用TPL/Tasks,我可以使用内部try/catch语句执行异常处理: Task.Factory.StartNew( ()=> { try { // Do stuff } catch { // Handle exception } }); 或者使用ContinueWith,如下所示: Task.Factory.St
Task.Factory.StartNew(
()=>
{
try
{
// Do stuff
}
catch
{
// Handle exception
}
});
或者使用ContinueWith,如下所示:
Task.Factory.StartNew(
()=>
{
// Do stuff
}).ContinueWith(
task =>
{
if(task.Exception != null)
// Handle exception
});
建议使用哪种方法?
每种方法的优缺点是什么?在某种程度上,这是一个偏好的问题,特别是如果您“拥有”任务代码和调用代码。这里有一些要考虑的事情。< /P> 首先,你应该。无论您是通过继续操作还是在操作中使用try/catch来处理它们,这都适用 另请注意有关未捕获异常的信息。从“纯粹主义”方法(在未捕获的任务异常情况下中断流程)到不那么严厉的方法,这种改变是有效的。然而,故意依赖这种新的行为是不好的 对于两个选项中的哪一个,选择第二个选项有一个论据:在延续中处理异常。在.NET中,返回任务的方法将越来越常见。例如要正确使用这些方法,您需要一个延续(传统的方法,或者使用带有新的
await
功能的try/catch块,这相当于一件事,但更易于编码和读取)。因此,最好养成这样的习惯:假设任何任务
都可能失败,除非您明确知道其他情况,并编写适当的异常处理行为
如果您感兴趣,这里有一种在.NET4.5中编写第二个示例的替代方法
async Task MyMethod()
{
try
{
await Task.Run(
() =>
{
// Some work.
});
}
catch (SomeException ex)
{
}
}
另一个差异最常见于Windows窗体或WPF应用程序中,在这些应用程序中,您的代码是从UI线程调用的。这里,当使用wait
时,TPL的默认行为是使用同步上下文运行continuations,该上下文将continuations封送回UI线程。也就是说,如果您的任务.Run
是从UI线程调用的,则延续也将在UI线程上运行
如果要向用户显示对话框以响应异常,此选项非常有用。您将无法在
任务
worload中成功执行此操作。当使用显式continuations而不是wait
时,必须将使用创建的TaskScheduler
传递到相应的重载。我认为这取决于上下文。如前所述,您应该只处理您知道如何处理的异常。我想说,如果你知道如何处理异常,你应该处理它
例如,如果您有一个任务,该任务应该从文件中加载一些数据,或者返回到某个默认值(这可能引发异常),一种方法是(伪代码):
对于
文件.Open
,我们知道如何继续。对于GetFallBackValue()
我们没有,因此我们将其传播给调用方,说明我们处于不一致的状态。您的两个示例在概念上是不同的
第一个在执行任务的内部处理异常。捕获后运行的任何代码仍将被执行
第二个调度程序调度另一个异步任务,该任务将始终在第一个任务完成后由调度程序运行
我猜答案是,这完全取决于你想要实现什么——没有明确的答案——但第二个答案更符合tpl
另外,在第二个示例中,task.IsFaulted比task.Exception更清楚,这主要取决于您的设计需要什么。需要考虑的一些事项: 捕获抛出异常的任务中的异常
- 当任务表示某个不可分割的工作单元时,包括在特定异常类型之后进行清理
- 当特定异常类型由于某种原因不应传播到任务之外时,例如,它需要包装在不同类型的外部异常中,以满足客户机代码对合同的期望
- 当异常清理应由不同的
任务调度器调度时,例如,在线程池上运行“主”任务,但将所有异常日志整理到UI线程上
- 如果有多个连续体有意义的话,每个连续体都会做不同的事情,但有例外,尽管这有点不寻常
- 确保观察并适当处理未提供代码的
任务的异常情况,例如,在
TaskFactory.FromAsync创建任务后正确清理。尽管这取决于具体情况,但也可以通过等待
任务来完成
请注意,如果要使用处理异常的延续,可以使用
TaskContinuationOptions。OnlyOnFaulted
仅在任务引发异常时运行延续,而不是在延续的定义内执行检查。感谢您提供的信息,但这与我的问题有什么关系?你的问题是“哪一个更值得推荐”,我说“这取决于上下文”,并用一个例子进行了解释。W
Task.Factory.StartNew(()=>
{
MyObject objectToSet = null;
try
{
objectToSet = File.Open("mydata");
}
catch (FileException ex)
{
// this will catch the FileException because we know how to handle that!
// the following will however throw an exception that we cannot handle
objectToSet = GetFallBackValue();
}
// when we are here we promise that the objectToSet is valid.
});