8

我有这个简单的代码:

var g=  Task.Factory.StartNew<int> (() => 8)
       .ContinueWith (ant =>{throw null;})
       .ContinueWith (a =>{ Console.WriteLine("OK");},TaskContinuationOptions.NotOnFaulted);

 try{
      Console.WriteLine("1");
      g.Wait();
      Console.WriteLine("2");
     }

catch (AggregateException  ex)
      {Console.WriteLine("catch"); }

输出 :

1
catch
System.AggregateException: A Task's exception(s) were not observed either by Waiting on the Task or accessing its Exception property. As a result, the unobserved exception was rethrown by the finalizer thread.

微博:

TaskContinuationOptions.NotOnFaulted

指定如果其前件引发未处理的异常,则不应安排继续任务。此选项对多任务延续无效。

行 。

在此处输入图像描述

没关系 - 不显示这一行会导致上一行 DID 抛出异常。

问题 :

  • AggregateException我是否因为我没有检查过Exception房产而得到例外?

  • 我是否必须始终检查先行项是否抛出异常(在每一行中?)?(我不能检查每一行!这没有任何意义而且很烦人

  • 块不try catch应该吞下异常吗?(我认为所有异常都会冒泡到等待方法......所以?)

4

3 回答 3

5

AggregateException我是否因为我没有检查过Exception房产而得到例外?

不,您会得到一个异常,因为gTPL 取消了任务(因为,如 msdn 所述,如果前面的任务引发异常,则不会安排此任务)。

我们在这里有 3 个任务:

  1. 原始任务(使用 StartNew)
  2. 第一个继续任务(引发异常)
  3. 第二个继续任务(打印 OK)(这是代码中的 g 任务)。

问题是您要求 TPL仅在第二个任务成功完成时才开始 3d 任务。这意味着如果不满足此条件,TPL 将完全取消您新创建的任务。

您有未观察到的任务异常,因为您有从未观察到的临时任务(我的列表中的任务 2)。一个因为你从来没有观察到它的错误状态,它会抛出终结器来告诉你它。

您可以通过在 catch 块中打印任务的状态来检查这一点:

catch (AggregateException ex)
{ 
    Console.WriteLine("catch");
    // Will print: Status in catch: Canceled
    Console.WriteLine("Status in catch: {0}", g.Status);
}

我是否必须始终检查先行项是否抛出异常(在每一行中?)?(我不能检查每一行!这没有任何意义而且很烦人)

是的,您应该观察先前的任务异常以避免此问题:

static class TaskEx
{
    public static Task ObserverExceptions(this Task task)
    {
        task.ContinueWith(t => { var ignore = t.Exception; },
                            TaskContinuationOptions.OnlyOnFaulted);
        return task;
    }
}

然后按如下方式使用它:

var g=  Task.Factory.StartNew<int> (() => 8)
       .ContinueWith (ant =>{throw null;})
       .ObserveExceptions()
       .ContinueWith (a =>{ Console.WriteLine("OK");});

 try{
      Console.WriteLine("1");
      g.Wait();
      Console.WriteLine("2");
     }

catch (AggregateException  ex)
      {Console.WriteLine("catch"); }

更新:为最后一个项目添加了解决方案

try catch 块不应该吞下异常吗?(我认为所有异常都会冒泡到等待方法......所以?)

我们的项目中有一组扩展方法(称为TransformWith),可以解决这个特定问题并获得以下收益:

  1. 异常会冒泡到 catch 块和
  2. 我们不会让应用程序崩溃TaskUnobservedException

这里的用法

var g = Task.Factory.StartNew(() => 8)
       .ContinueWith(ant => { throw null; })
       // Using our extension method instead of simple ContinueWith
       .TransformWith(t => Console.WriteLine("OK"));

try
{
    Console.WriteLine("1");
    // Will fail with NullReferenceException (inside AggregateExcpetion)
    g.Wait();
    Console.WriteLine("2");
}

catch (AggregateException ex)
{
    // ex.InnerException is a NullReferenceException
    Console.WriteLine(ex.InnerException);
}

这是一个扩展方法:

static class TaskEx
{
    public static Task TransformWith(this Task future, Action<Task> continuation)
    {
        var tcs = new TaskCompletionSource<object>();
        future
            .ContinueWith(t =>
            {
                if (t.IsCanceled)
                {
                    tcs.SetCanceled();
                }
                else if (t.IsFaulted)
                {
                    tcs.SetException(t.Exception.InnerExceptions);
                }
                else
                {
                    try
                    {
                        continuation(future);
                        tcs.SetResult(null);
                    }
                    catch (Exception e)
                    {
                        tcs.SetException(e);
                    }
                }
            }, TaskContinuationOptions.ExecuteSynchronously);

        return tcs.Task;
    }    
}
于 2012-12-06T09:48:43.790 回答
3
  • 我是否因为没有检查 Exception 属性而得到 AggregateException 异常?

任务总是抛出 AggregateException :http: //msdn.microsoft.com/en-us/library/system.threading.tasks.task.exception.aspx

您可以使用以下方法获取原始异常:

var myTask = Task.Factory.StartNew(() => { throw new NotImplementedException(); });
var myException = myTask.Exception.Flatten().InnerException as NotImplementedException;
  • 我是否必须始终检查先行项是否抛出异常(在每一行中?)?(我不能检查每一行!这没有任何意义而且很烦人)

是的,这很烦人,您应该为每个任务创建两个延续来检查异常:一个检查是否有异常来处理它,另一个在没有异常的情况下继续操作,请参见TaskContinuationOptions.OnlyOnFaultedand TaskContinuationOptions.OnlyOnRanToCompletion。如果需要,您甚至应该创建第三个延续来处理取消。

  • try catch 块不应该吞下异常吗?(我认为所有异常都会冒泡到等待方法......所以?)

不,不会,更高级别不会引发异常,您应该TaskContinuationOptions.OnlyOnFaulted在任务继续中使用以检查是否存在异常。您只能使用.net 4 中不可用的async关键字在调用者级别获取任务异常

于 2012-12-06T09:51:51.153 回答
0

像这样处理 AggregateExceptions:

catch(AggregateException aex)
{
    aex.Handle(ex =>
    {
       // Do some handling and logging
        return true;
    }
}
于 2012-12-06T18:21:37.650 回答