如何在ThreadAbortException后恢复
本文关键字:恢复 ThreadAbortException | 更新日期: 2023-09-27 18:06:14
我在一个MVC3应用程序中有几个长时间运行的线程,它们打算永远运行。
我遇到了一个问题,一个ThreadAbortException正在被一些其他代码(不是我的)调用,我需要从这个优雅地恢复并重新启动线程。现在,我们唯一的办法是为appDomain回收工作进程,这远非理想。
下面是这段代码的一些细节:
这个mvc应用程序存在一个单例服务类。它必须是单例的,因为它缓存数据。该服务负责向数据库发出请求。实际的数据库连接代码使用第三方库。
在这个单例类中,我们使用了一个名为"QueryRequestors"的类集合。这些类为数据库请求标识唯一的包+stored_procedure名,以便我们可以对这些调用进行排队。这就是QueryRequestor类的目的:确保对同一个package+stored_procedure(尽管它们可能有无限个不同的参数)的调用是排队的,并且不会同时发生。这大大减轻了数据库的压力并提高了性能。
QueryRequestor类使用一个内部的BlockingCollection和一个内部的Task(线程)来监视它的队列(阻塞集合)。当请求进入单例服务时,它通过包+stored_procedure名找到正确的QueryRequestor类,并将查询交给该类。查询被放入队列(阻塞集合)。QueryRequestor的Task看到队列中有一个请求,并对数据库进行调用(现在涉及到第三方库)。当结果返回时,它们被缓存在单例服务中。Task继续处理请求,直到阻塞集合为空,然后等待。一旦QueryRequestor被创建并启动并运行,我们永远不希望它死亡。请求每隔几分钟就会进入这项服务。如果服务中的缓存有数据,我们就使用它。当数据过期时,下一个请求进入队列(随后的并发请求继续使用缓存,因为它们知道有人(另一个线程)已经在发出队列请求,这是高效的)。
所以这里的问题是当QueryRequestor类中的Task遇到ThreadAbortException时该怎么做。理想情况下,我想从中恢复并重新启动线程。或者,至少,处理QueryRequestor(据我所知,它现在处于"中断"状态)并重新开始。因为如果服务中没有QueryRequestor,那么与包+stored_procedure名称匹配的下一个请求将创建一个新的QueryRequestor。
我怀疑线程正在被第三方库杀死,但我不能确定。我所知道的是,I在任何地方都不会中止或试图杀死线程/任务。我想让它永远运行下去。但是很明显,我们必须为这个异常准备代码。当服务因为线程被中止而爆炸时,这是非常烦人的。
处理这个问题的最好方法是什么?我们如何才能优雅地处理这个问题?
您可以通过调用Thread.ResetAbort来停止重新抛出ThreadAbortException
。
请注意,最常见的异常情况是重定向调用,取消线程中止可能会导致执行请求代码的不良影响,否则由于终止线程而被忽略。与MVC(你可以从控制器返回特殊的重定向结果)相比,这在WinForms(代码和渲染的分离不太清楚)中是常见的问题。
这是我想出的解决方案,它工作得很好。
这里真正的问题不是阻止ThreadAbortException,因为你无法阻止它,我们也不想阻止它。如果我们得到一个错误报告告诉我们发生了这个,这实际上是一件好事。我们只是不希望我们的应用因此而宕机。
所以,我们真正需要的是一种优雅的方式来处理这个异常,而不会使应用程序崩溃。
我想出的解决方案是在QueryRequestor类上创建一个bool标志属性,称为"IsValid"。此属性在类的构造函数中设置为true。
在运行在QueryRequestor类的单独线程上的DoWork()调用中,我们捕获ThreadAbortException并将此标志设置为FALSE。现在我们可以告诉其他代码,这个类处于无效(破碎)状态,不要使用它。
现在,使用这个QueryRequestor类的单例服务知道要检查这个IsValid属性。如果无效,则用新的QueryRequestor替换QueryRequestor,然后继续工作。应用程序不会崩溃,损坏的QueryRequestor被丢弃,取而代之的是一个可以完成这项工作的新版本。
在测试中,这工作得很好。我会故意在DoWork()线程上调用thread . abort(),并观察Debug窗口中的输出行。应用程序将报告线程已经中止,然后单例服务正确地替换了QueryRequestor。然后,替换程序就能够成功地处理请求。