处理Tpl中的异常

6

我已经阅读了很多关于如何在TPL中处理异常的内容,但并不是很理解。

我们来看这个示例代码:

var task1 = new Task(() => { throw new Exception("Throw 1"); });
var task2 = task1.ContinueWith(t => Console.WriteLine("Catch 1:{0}", t.Exception.Message), 
                              TaskContinuationOptions.OnlyOnFaulted);
var task3 = task2.ContinueWith(t => Console.WriteLine("Continuation"));

task1.Start();
try {
    task1.Wait();
}
catch (Exception ex) {
    Console.WriteLine("Wait Exception: {0}", ex.Message);
}

我期望这个会被打印出来

Catch 1
Continuation

但我获得
Catch 1
Continuation
Wait Exception

这意味着当任务完成并且任务终止程序时,异常仍然被视为未处理。如何在延续中处理异常以便于最终器不会抛出呢?同时我希望任务仍处于故障状态,因此在 try/catch 中包装任务是行不通的。背景是我想要按照这里指定的异步事件模式实现错误处理。完整代码如下:
public IAsyncResult Begin(AsyncCallback callback, object state, Action action) {
    var task1 = new Task(action);
    var task2 = task1.ContinueWith(t => HandleException(t.Exception), 
                                   TaskContinuationOptions.OnlyOnFaulted);
    if (callback != null) {
        var task3 = task2.ContinueWith(t => callback(t),
                                      TaskScheduler.FromCurrentSynchronizationContext());
        var task4 = task3.ContinueWith(t => HandleException(t.Exception), 
                                       TaskContinuationOptions.OnlyOnFaulted);
    }

    task1.Start();

    return task;
}

我考虑在延续中的try块中调用t.Wait()并忽略异常。但这样做行不通,因为延续可能会在另一个Wait()抛出异常后执行。 - svick
1个回答

3

如果您在等待失败的任务上使用了Task.Wait,仔细阅读文档,您会发现在这种情况下,等待将重新抛出异常。

但是,如果您在等待task3,则一切都应该按预期工作。

当然,您应该记住以下内容:

当您使用OnlyOnFaulted选项时,可以保证前置任务中的Exception属性不为null。 您可以使用该属性捕获异常并查看哪个异常导致任务失败。 如果您不访问Exception属性,则异常将无法处理。 此外,如果您尝试访问已取消或已故障的任务的Result属性,将引发新异常。

(参考此处)

最后,另一个关于如何处理任务抛出的异常的好资源。

希望这可以帮助您。


1
我想我明白了。即使我访问.Exception属性,等待也会引发异常。但是,访问.Exception会将异常更改为不是“未处理的”,因此任务终结器不会抛出异常。一个快速的测试似乎证实了这一点。谢谢。 - adrianm

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接