简体   繁体   English

任务 <T> 和C#中的TaskContinuationOptions澄清?

[英]Task<T> and TaskContinuationOptions Clarification in C#?

I have this simple code : 我有这个简单的代码:

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

The Output : 输出 :

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 : msdn:

TaskContinuationOptions.NotOnFaulted TaskContinuationOptions.NotOnFaulted

Specifies that the continuation task should not be scheduled if its antecedent threw an unhandled exception. 指定如果延续任务的前端抛出未处理的异常,则不应该对其进行调度。 This option is not valid for multi-task continuations. 此选项对多任务延续无效。

ok . 好 。

在此输入图像描述

And it is ok - not showing this line cause the prev line DID throw exception. 并且没关系 - 不显示此行导致prev行DID抛出异常。

Questions : 问题:

  • Do I get the AggregateException exception because I haven't inspected the Exception property ? 我是否收到了AggregateException异常,因为我没有检查过Exception属性?

  • Must I always inspect if the antecedent throw an exception ( in each line ? ) ? 我是否必须始终检查先前是否会抛出异常(在每一行?)? ( I can't check each line ! it doesn't make any sense and very annoying ) 我不能检查每一行!它没有任何意义,非常烦人

  • Wasn't the try catch block should have swallow the exception ? 是不是try catch块应该吞下异常? ( I thought that all exceptions bubble up to the wait method....so ? ) (我认为所有异常都会导致等待方法....所以?)

Do I get the AggregateException exception because I haven't inspected the Exception property ? 我是否收到了AggregateException异常,因为我没有检查过Exception属性?

No, you get an exception, because task g cancels by TPL(because, as msdn stated, this task will not scheduled if antescendent task throws an exception). 不,你得到一个例外,因为任务g被TPL取消(因为,如msdn所述,如果antescendent任务抛出异常,则不会安排此任务)。

We have 3 tasks here: 我们这里有3个任务:

  1. Original Task (that uses StartNew) 原始任务(使用StartNew)
  2. First Continuation Task (that throws an exception) 第一个继续任务(抛出异常)
  3. Second Continuation Task (that prints OK) (this is g task from your code). 第二个继续任务(打印正常)(这是您的代码中的g任务)。

The issue is that you ask TPL to start 3d task only if 2nd task will finished successfully . 问题是, 只有当第二个任务成功完成时 ,才会要求TPL启动3d任务。 This means that if this condition will not met TPL will cancel your newly created task entirely. 这意味着如果不满足此条件,TPL将完全取消您新创建的任务。

You got unobserved task exception because you have temporary task (task 2 in my list) that you never observe. 你有未被观察到的任务异常,因为你有一个你从未观察过的临时任务(我的列表中的任务2)。 An because you never observe it faulted state it will throw in finalizer to tell you about it. 因为你从来没有观察过它的故障状态,它会抛出终结器来告诉你它。

You can check this by printing task's status in catch block: 您可以通过在catch块中打印任务的状态来检查:

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

Must I always inspect if the antecedent throw an exception ( in each line ? ) ? 我是否必须始终检查先前是否会抛出异常(在每一行?)? ( I can't check each line ! it doesn't make any sense and very annoying) (我不能检查每一行!它没有任何意义,非常烦人)

Yes you should observe antecedent tasks exception to avoid this issue: 是的,您应该观察先前的任务异常以避免此问题:

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

And then use it as following: 然后使用它如下:

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

UPDATE: Added solution to last bullet 更新:为最后一颗子弹添加了解决方案

Wasn't the try catch block should have swallow the exception ? 是不是try catch块应该吞下异常? ( I thought that all exceptions bubble up to the wait method....so ? ) (我认为所有异常都会导致等待方法....所以?)

We have set of extension method (called TransformWith ) in our project that can solve this particular issue and gain following: 我们在项目中有一组扩展方法(称为TransformWith ),可以解决这个特定问题,并获得以下内容:

  1. Exception would bubble up to the catch block and 异常会冒泡到catch块和
  2. We'll not crash application with TaskUnobservedException 我们不会使用TaskUnobservedException崩溃应用程序

Here the usage 这里的用法

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

And here is a extension method: 这是一个扩展方法:

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;
    }    
}
  • Do I get the AggregateException exception because I haven't inspected the Exception property ? 我是否收到了AggregateException异常,因为我没有检查过Exception属性?

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

You can get the original exception using : 您可以使用以下方式获取原始异常:

var myTask = Task.Factory.StartNew(() => { throw new NotImplementedException(); });
var myException = myTask.Exception.Flatten().InnerException as NotImplementedException;
  • Must I always inspect if the antecedent throw an exception ( in each line ? ) ? 我是否必须始终检查先前是否会抛出异常(在每一行?)? ( I can't check each line ! it doesn't make any sense and very annoying) (我不能检查每一行!它没有任何意义,非常烦人)

Yes it is anoying, you should create two continuations for each task to check exceptions : one that checks if there has been an exception to handle it, and another one to continue the operation if there was no exception see TaskContinuationOptions.OnlyOnFaulted and TaskContinuationOptions.OnlyOnRanToCompletion . 是的,这很烦人,你应该为每个任务创建两个延续来检查异常:一个检查是否有异常来处理异常,另一个继续操作,如果没有异常,请参阅TaskContinuationOptions.OnlyOnFaultedTaskContinuationOptions.OnlyOnRanToCompletion You should even create a third continuation to deal with cancellation if needed. 如果需要,您甚至应该创建第三个延续来处理取消。

  • Wasn't the try catch block should have swallow the exception ? 是不是try catch块应该吞下异常? ( I thought that all exceptions bubble up to the wait method....so ? ) (我认为所有异常都会导致等待方法....所以?)

No it won't, exceptions are not thrown at higher level, you should use TaskContinuationOptions.OnlyOnFaulted on the task continuation to check if there was an exception. 不,它不会,异常不会在更高级别抛出,您应该在任务延续上使用TaskContinuationOptions.OnlyOnFaulted来检查是否存在异常。 You can get tasks exceptions at caller's level only with the async keyword not available in .net 4 只能使用.net 4中不可用的async关键字,才能在调用者级别获取任务异常

Handle AggregateExceptions like this: 像这样处理AggregateExceptions:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM