当使用长时间运行的后台消费任务时,task. factory . startnew中的静默异常
本文关键字:factory task startnew 异常 静默 长时间 运行 后台 任务 | 更新日期: 2023-09-27 18:18:49
这将通知一个未处理的异常:
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
处于什么状态?
我知道我可以使用延续与TaskContinuationOptions。OnlyOnFaulted,你能恢复处理吗?
那么,你认为会发生什么呢?您是否认为每当在另一个线程中抛出异常时,它应该立即传播到启动该任务的线程?我强烈反对。首先,调用线程中的代码将被迫中止它正在执行的操作,这很可能导致严重的问题。只要看看有关Thread.Abort
的所有帖子,就会发现当您允许在程序执行的任意点抛出异常而不是在某些已知点抛出异常时,会出现所有非常重要的问题。
如果你建议当一个任务的代码抛出异常时,整个程序应该崩溃,那么我想说总的来说,这是不可取的。在极少数情况下,您可以(相当容易地)在任务上创建延续,在任务出错时结束整个流程。我还没有需要创建这样一个延续自己。如果系统被设计为在任务抛出异常时关闭进程,那么获得相反的行为将不会那么容易。
抛出异常的线程发生了什么?它会死吗?
如果异常被捕获,则在捕获后继续执行;如果它通过整个调用堆栈传播,那么异常将被Task
框架内的代码捕获,该框架包装异常并使其可用于延续。
在这种情况下,_operationQueue处于什么状态?
这是一个非常好的队列,它有1..从中移除N个项目。如果循环体总是抛出,那么就会从循环体中取出一个元素。如果它只是偶尔抛出,那么一定数量的物品将从中取出。剩余的项仍然在队列中,可以被任何其他访问它的线程删除。如果队列不再可访问,那么它将有资格进行垃圾收集。
我知道我可以使用延续与TaskContinuationOptions。OnlyOnFaulted,你能恢复处理吗?
调用线程可以;确定。任务本身只有在其委托中拥有try/catch
才能继续。
任务中的异常被TaskScheduler
捕获。如果您永远不想观察任务的结果,但希望在任务中未处理的异常时得到通知,那么可以使用TaskScheduler.UnobservedTaskException
事件。请注意,如果从未检索到异常,则此事件不会立即触发,而是在Task
结束时触发。在。net 4中,未观察到的任务异常被重新抛出并成为未处理的异常,从而结束进程,但在。net 4.5中对此进行了更改。