任务并行库和例外?

时间:2012-08-16 15:25:42

标签: .net task-parallel-library

我应该这样做吗?

int x = 0;
Task<int> calc = Task.Factory.StartNew (() => 7 / x);
try
{
  Console.WriteLine (calc.Result);
}
catch (AggregateException aex)
{
  Console.Write (aex.InnerException.Message);  // Attempted to divide by 0
}

还是这个?

int x = 0;
try
{
  Task<int> calc = Task.Factory.StartNew (() => 7 / x);
  Console.WriteLine (calc.Result);
}
catch (AggregateException aex)
{
  Console.Write (aex.InnerException.Message);  // Attempted to divide by 0
}

如果任务立即开始,然后在我们进入try catch块之前,我们就不会抓住它......!

4 个答案:

答案 0 :(得分:1)

使用Task的一个要点是你大多不必担心这样的事情。

正如您所注意到的,第一个样本有两种可能的事件顺序:

    从线程A调用
  1. StartNew()
  2. 从线程A调用
  3. Result getter。任务尚未完成,因此调用阻塞。
  4. 委托在ThreadPool线程B上执行并抛出DivideByZeroException
  5. 线程A醒来并Result抛出AggregateException
  6. 第二种可能性是:

      从线程A调用
    1. StartNew()
    2. 委托在ThreadPool线程B上执行并抛出DivideByZeroException
    3. 从线程A调用
    4. Result getter。任务已经完成,因此调用会立即抛出AggregateException
    5. 如您所见,在两种情况下Result getter抛出异常,代码执行的顺序无关紧要。

      只有StartNew()可以抛出AggregateException,你的第二个版本才有意义,但这种情况永远不会发生。

      让我再说一遍:TPL负责所有的同步,你不必在此担心。

答案 1 :(得分:0)

任务有一个Exception属性,用于保存在任务执行期间抛出的异常(如果有的话)。这意味着你可以这样做:

int x = 0;
Task<int> calc = Task.Factory.StartNew(() => 7 / x);
calc.ContinueWith(t => Console.WriteLine ("Got exception of {0}", t.Exception),
                  TaskContinuationOptions.OnlyOnFaulted);

答案 2 :(得分:0)

您希望在try块中执行尽可能少的代码,因为您包含的内容越多,您将捕获的虚假,不需要的异常。你只想抓住那些你知道他们是良性的。你不想吞下虫子。

因此要么这样做:

var task = ...;
int result;
try { result = task.Result; } //just catch task.Result
...

甚至是这样:

if (task.Exception != null) { /* error */ }
else { /* use task.Result */ }

答案 3 :(得分:0)

在这种情况下,您不需要将其包含在try块中。除非你传递一个null Action来执行,否则Task.Factory.StartNew不会抛出异常。正在运行的操作抛出的任何异常都将传播到任务中,而不是传播到堆栈中。

更一般地说,我认为这些类型的情况的经验法则是异常应该总是进入任务。例如,如果在异步方法中的第一个await之前抛出异常,它也会进入生成的任务,而不是向上传播堆栈。