ThreadAbortException后如何恢复?

时间:2013-03-27 17:51:17

标签: c# multithreading asp.net-mvc-3 iis exception-handling

我在MVC3应用程序中有几个长时间运行的线程,它们将永远运行。

我遇到了一个问题,其中一些其他代码(不是我的)调用了ThreadAbortException,我需要优雅地从中恢复并重新启动线程。现在,我们唯一的办法是回收appDomain的工作进程,这远非理想。

以下是有关此代码的一些详细信息:

此MVC3应用程序存在单例服务类。它必须是单例,因为它缓存数据。该服务负责向数据库发出请求。第三方库用于实际的数据库连接代码。

在这个单例类中,我们使用一组名为“QueryRequestors”的类。这些类为数据库的请求标识唯一的package + stored_procedure名称,以便我们可以对这些调用进行排队。这就是QueryRequestor类的目的:确保对同一个包+ stored_procedure的调用(尽管它们可能具有无限不同的参数)排队,并且不会同时发生。这大大减轻了我们的数据库压力并提高了性能。

QueryRequestor类使用内部BlockingCollection和内部Task(线程)来监视其队列(阻塞集合)。当请求进入单例服务时,它通过包+ stored_procedure名称找到正确的QueryRequestor类,并将查询交给该类。查询被放入队列(阻塞集合)。 QueryRequestor的Task看到队列中有一个请求并调用数据库(现在涉及第三方库)。当结果返回时,它们被缓存在单例服务中。任务继续处理请求,直到阻塞集合为空,然后等待。

一旦QueryRequestor被创建并启动并运行,我们永远不会希望它死掉。请求每隔一分钟24/7进入此服务。如果服务中的缓存有数据,我们就会使用它。当数据过时时,下一个请求将排队(并且后续的同时请求继续使用缓存,因为他们知道有人(另一个线程)已经在排队请求,这很有效)。

所以这里的问题是当QueryRequestor类中的Task遇到ThreadAbortException时该怎么办。理想情况下,我想从中恢复并重新启动线程。或者,至少,处理QueryRequestor(就我而言,它现在处于“破碎”状态)并重新开始。因为与package + stored_procedure名称匹配的下一个请求将创建一个新的QueryRequestor(如果服务中不存在)。

我怀疑该线程被第三方库杀死了,但我无法确定。我所知道的是 I 中止或者试图杀死线程/任务。我希望它永远运行。但显然我们必须为此例外准备代码。当服务遭到轰炸,因为一个线程被中止,这非常烦人。

处理此问题的最佳方法是什么?我们怎样才能优雅地处理这个问题?

2 个答案:

答案 0 :(得分:2)

您可以致电Thread.ResetAbort停止重新投掷ThreadAbortException

请注意,异常的最常见情况是重定向调用,并且取消线程中止可能会导致执行请求代码的不良影响,否则将因为终止线程而被忽略。 WinForms中的常见问题(代码和渲染的分离不太清晰)比MVC(可以从控制器返回特殊的重定向结果)。

答案 1 :(得分:1)

这是我提出的解决方案,它运作得非常好。

这里真正的问题不是阻止ThreadAbortException,因为无论如何都无法阻止它,我们不想阻止它。如果我们得到一个错误报告告诉我们这件事发生了,这实际上是一件好事。我们只是不希望我们的应用程序因此而下降。

因此,我们真正需要的是一种优雅的方式来处理此异常而不会导致应用程序崩溃。

我想出的解决方案是在QueryRequestor类上创建一个名为“IsValid”的bool标志属性。此属性在类的构造函数中设置为true。

在QueryRequestor类中的单独线程上运行的DoWork()调用中,我们捕获ThreadAbortException并将此标志设置为FALSE。现在我们可以告诉其他代码这个类处于无效(损坏)状态而不使用它。

现在,使用此QueryRequestor类的单例服务知道检查此IsValid属性。如果它无效,它将用新的QueryRequestor替换QueryRequestor,并继续生活。应用程序不会崩溃,破坏的QueryRequestor会被丢弃,取而代之的是可以完成工作的新版本。

在测试中,这非常有效。我故意在DoWork()线程上调用Thread.Abort(),并观察输出行的Debug窗口。应用程序将报告线程已中止,然后单例服务正在替换QueryRequestor。然后,替换人员能够成功处理请求。