任务<;T>;以及C#中的TaskContinuationOptions说明?

任务<;T>;以及C#中的TaskContinuationOptions说明?,c#,.net,multithreading,.net-4.0,task-parallel-library,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");

我有一个简单的代码:

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"); }
msdn:

TaskContinuationOptions.NotUnfault

指定如果继续任务的 antecedent引发了未处理的异常。此选项对无效 多任务连续性

这是正常的-不显示这一行导致上一行抛出异常

问题:

  • 我是否因为没有检查
    异常
    属性而获得
    聚合异常
    异常

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

  • try-catch
    块不应该包含异常吗?(我认为所有的异常都会出现在等待方法中……所以呢?)

我是否因为没有检查而获得了
aggregateeexception
异常
异常
属性

不,您会得到一个异常,因为任务
g
由TPL取消(因为,如msdn所述,如果Antescedent任务引发异常,则不会计划此任务)

我们有3项任务:

  • 原始任务(使用StartNew)
  • 第一个继续任务(引发异常)
  • 第二个继续任务(打印OK)(这是代码中的g任务) 问题是,只有在第二个任务成功完成时,您才要求TPL启动3d任务。这意味着,如果不满足此条件,TPL将完全取消您新创建的任务

    您得到了未观察到的任务异常,因为您有一个从未观察到的临时任务(我列表中的任务2)。因为您从未观察到它的故障状态,所以它将抛出终结器来告诉您。

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

    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"); }
    
    下面是一个扩展方法:

    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;
        }    
    }
    
    静态类TaskEx
    {
    公共静态任务转换(此任务未来,操作继续)
    {
    var tcs=new TaskCompletionSource();
    未来
    .ContinueWith(t=>
    {
    如果(t.IsCanceled)
    {
    setCancelled();
    }
    否则,如果(t.IsFaulted)
    {
    SetException(t.Exception.innerException);
    }
    其他的
    {
    尝试
    {
    延续(未来);
    tcs.SetResult(空);
    }
    捕获(例外e)
    {
    tcs.SetException(e);
    }
    }
    },TaskContinuationOptions。同步执行);
    返回tcs.Task;
    }    
    }
    
    • 我是否因为没有检查而获得AggregateException异常 异常属性

    任务始终引发AggregateException:

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

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

    是的,这是一个问题,您应该为每个任务创建两个连续性来检查异常:一个检查是否存在异常以处理异常,另一个检查是否存在异常以继续操作请参见
    TaskContinuationOptions。OnlyOnFaulted
    TaskContinuationOptions。OnlyOnRanToCompletion
    。 如果需要的话,您甚至应该创建第三个续集来处理取消

    • try-catch块不应该吞下异常吗?(一) 认为所有异常都会出现在等待方法中……那么呢?)
    不,不会,不会在更高级别引发异常,您应该使用
    TaskContinuationOptions。仅在任务继续上使用onfaulted
    ,以检查是否存在异常。只有在.net 4中关键字不可用的情况下,才能在调用方级别获取任务异常


    处理聚合异常,如下所示:

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

    唉,这个话题并不罕见。我在我的自定义
    TaskEx
    类中使用了几种扩展方法来解决这个问题。顺便说一句,你可以看看Joe Albahari关于这个主题的伟大文章:对于这类问题有一个很好的描述。我的意思是,不幸的是,这不是一个罕见的主题。我不知道,因为如果你从这里举一个例子,乔也给了你解决方案,并清楚地描述了这种行为。@SergeyTeplyakov不,他没有。他谈到检查异常属性(如果您不检查,您将得到异常),但他没有谈到以流畅的方式检查异常。他创建了task1和task2。这与我的示例不同。Sergey,关于您的上一个代码:如果我只使用
    有故障的
    ——我的应用程序不会死(对吗?),因为
    异常
    属性已被检查。因此,我可以
    抛出myNewException
    ,它将被缓存在
    catch
    子句中(我的应用程序仍然不会死)对吗?如果您在最后一个连续中只添加了一个Faulted,您仍然会得到AggregateException,因为g会将其状态更改为Cancelled。@RoyiNamir:我更改了澄清:“另一个问题是您有临时任务(我列表中的任务2),但您从未观察到。因为您从未观察到它的错误状态,所以它将抛出终结器来告诉您。否。一旦您允许异常(
    var ignore=t.exception;
    )yo
    var myTask = Task.Factory.StartNew(() => { throw new NotImplementedException(); });
    var myException = myTask.Exception.Flatten().InnerException as NotImplementedException;
    
    catch(AggregateException aex)
    {
        aex.Handle(ex =>
        {
           // Do some handling and logging
            return true;
        }
    }