C <?>中的任务<t>和TaskContinuationOptions澄清?</t>

时间:2012-12-06 08:40:42

标签: c# .net multithreading .net-4.0 task-parallel-library

我有这个简单的代码:

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.

msdn:

TaskContinuationOptions.NotOnFaulted

  

指定不应安排延续任务   先行者抛出了一个未经处理的例外。此选项无效   多任务延续。

好的。

enter image description here

没关系 - 不显示此行会导致prev行DID抛出异常。

问题:

  • 我是否收到AggregateException例外,因为我没有检查Exception属性?

  • 我必须始终检查前提是否会抛出异常(在每一行?)? (我无法检查每一行!它没有任何意义且非常烦人

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

3 个答案:

答案 0 :(得分:5)

  

我是否收到AggregateException例外,因为我没有检查过   Exception属性?

不,你得到一个例外,因为任务g被TPL取消(因为,如msdn所述,如果antescendent任务抛出异常,则不会安排此任务。)

我们这里有3个任务:

  1. 原始任务(使用StartNew)
  2. 第一个继续任务(抛出异常)
  3. 第二个继续任务(打印正常)(这是代码中的g任务)。
  4. 问题是,只有第二项任务成功完成,才会要求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
    3. 崩溃申请

      这里的用法

      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;
          }    
      }
      

答案 1 :(得分:3)

  •   

    我是否收到了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;
  •   

    我必须始终检查先前是否会抛出异常(在每个中   线? )? (我不能检查每一行!它没有任何意义和   非常讨厌)

是的,这很烦人,你应该为每个任务创建两个延续来检查异常:一个检查是否有异常来处理异常,另一个继续操作,如果没有异常,请参阅{{1} }和TaskContinuationOptions.OnlyOnFaulted。 如果需要,你甚至应该创建第三个延续来处理取消。

  •   

    是不是try catch块应该吞下异常? ( 一世   以为所有异常都会导致等待方法....所以? )

  •   
  不,它不会,异常不会在更高级别抛出,您应该在任务延续时使用TaskContinuationOptions.OnlyOnRanToCompletion来检查是否存在异常。只有在.net 4

中没有的async关键字,您才能在来电者级别获取任务例外情况

答案 2 :(得分:0)

处理像这样的AggregateExceptions:

catch(AggregateException aex)
{
    aex.Handle(ex =>
    {
       // Do some handling and logging
        return true;
    }
}