1

我应该这样做吗?

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

4 回答 4

1

使用的要点之一Task是您几乎不必担心这样的事情。

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

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

第二种可能是:

  1. StartNew()从线程 A 调用。
  2. 委托在 ThreadPool 线程 B 上执行并抛出DivideByZeroException.
  3. Result从线程 A 调用 getter。任务已经完成,因此调用立即抛出AggregateException.

如您所见,在这两种情况下,Resultgetter 都会引发异常,代码执行的顺序无关紧要。

StartNew()您的第二个版本只有在可以 throw时才有意义AggregateException,但这永远不会发生。

让我重复一遍:TPL 负责所有同步,您不必在这里担心。

于 2012-08-16T16:23:39.147 回答
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);
于 2012-08-16T16:24:24.033 回答
0

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

因此,要么这样做:

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

甚至这个:

if (task.Exception != null) { /* error */ }
else { /* use task.Result */ }
于 2012-08-16T16:25:43.403 回答
0

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

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

于 2012-08-16T16:37:39.013 回答