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

jim*_*rra 5 .net c# exception task task-parallel-library

这通知未处理的异常:

new Thread(_ => { throw new Exception(); }).Start();
Run Code Online (Sandbox Code Playgroud)

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

Task.Factory.StartNew(() =>
        {
            throw new Exception();
        });
Run Code Online (Sandbox Code Playgroud)

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

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

_operationQueue = new BlockingCollection<Operation>();

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

在这种情况下,_operationQueue左边是什么状态?

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

Ser*_*rvy 9

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

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

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

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

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

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

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

调用线程可以; 当然.任务本身只能通过拥有一个try/catch代表内部来继续.如果异常被抛出到任务出错的程度,那么任何事情都不会允许该任务继续执行.