使用长时间运行的后台消费者任务时 Task.Factory.StartNew 中的静默异常?

2023-12-23

这通知未处理的异常:

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 的 Continuation,您可以恢复处理吗?


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

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

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

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

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

这是一个非常好的队列,已从中删除了 1..N 个项目。如果循环体总是抛出异常,那么将从循环体中取出一项。如果它只是偶尔抛出,那么将会从中取出一些物品。剩余的项目仍在队列中,并且可以由有权访问它的任何其他线程删除。如果队列不再可访问,那么它将有资格进行垃圾回收。

我知道我可以使用带有 TaskContinuationOptions.OnlyOnFaulted 的 Continuation,您可以恢复处理吗?

调用线程可以;当然。任务本身只能通过有一个try/catch在它的代表内。如果异常已引发到任务发生故障的程度,则任何任务都不会允许该任务继续执行。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

使用长时间运行的后台消费者任务时 Task.Factory.StartNew 中的静默异常? 的相关文章

随机推荐