使用长时间运行的后台消费者任务时,Task.Factory.StartNew中的无提示异常?

时间:2013-06-17 15:26:18

标签: c# .net exception task-parallel-library task

通知未处理的异常:

new Thread(_ => { throw new Exception(); }).Start();

这不会(至少在等待/检索结果之前):

Task.Factory.StartNew(() =>
        {
            throw new Exception();
        });

为什么呢?发生异常的线程发生了什么?它死了吗?

这是一个运行任务但不需要其结果或需要等待它的问题,如下所示:

_operationQueue = new BlockingCollection<Operation>();

Task.Factory.StartNew(() => 
{
     foreach (var item in _operationQueue.GetConsumingEnumerable())
     {
         // do something that throws
     }
}, TaskCreationOptions.LongRunning);

在这种情况下,_operationQueue处于什么状态?

我知道我可以使用Continuation with TaskContinuationOptions.OnlyOnFaulted,你能继续处理吗?

2 个答案:

答案 0 :(得分:9)

那么,你认为应该发生什么呢?你是否认为每当在另一个线程中引发异常时它应该立即传播到启动该任务的线程?我强烈不同意。首先,调用线程中的代码将被强制中止它在中间的操作,这很可能会导致严重的问题。只需查看Thread.Abort周围的所有帖子,即可看到当您允许在程序执行中的某个任意点而不是某些已知点处抛出异常时出现的所有非常重要的问题。

如果您建议整个程序在任务代码抛出异常时崩溃,那么我会说这根本不可取。在极少数情况下,如果任务出现故障,您可以(相当容易)在任务上创建一个延续整个过程的延续。我自己还不需要自己创造这样的延续。如果系统设计为在任务抛出异常时关闭进程,那么获得相反的行为就不那么容易了。

  

抛出异常的线程发生了什么?它死了吗?

如果捕获到异常,则在catch之后继续执行;如果它传播通过整个调用堆栈,则异常将被Task框架内的代码捕获,该代码包装异常并使其可用于延续。

  

在这种情况下,_operationQueue处于什么状态?

这是一个完美的队列,已经从中移除了1..N个项目。如果循环的主体总是抛出,那么将从中取出一个项目。如果它只是有时会抛出,那么将从中取出一些物品。其余项目仍在队列中,可以被任何其他有权访问的线程删除。如果队列不再可访问,那么它就有资格进行垃圾收集。

  

我知道我可以使用Continition with TaskContinuationOptions.OnlyOnFaulted,你能继续处理吗?

调用线程可以;当然。任务本身只能通过在其委托中拥有try/catch来继续。如果异常被抛出到任务出错的程度,那么任何事情都不会允许该任务继续执行。

答案 1 :(得分:3)

任务中的异常由TaskScheduler捕获。如果您不想观察任务的结果,但希望收到有关任务中未处理的异常的通知,则会发生TaskScheduler.UnobservedTaskException事件。请注意,此事件不会立即触发,但如果从未检索到异常,则在Task完成时触发。在.Net 4中,重新抛出了未观察到的任务异常,并成为结束该过程的未处理异常,但在.Net 4.5中已更改。