2

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

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

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

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

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

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

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

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

我怀疑线程被第 3 方库杀死,但我不能确定。我所知道的是,无处中止或试图杀死线程/任务。我希望它永远运行。但显然我们必须为这个异常准备好代码。当服务因为线程被中止而爆炸时,这很烦人。

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

4

2 回答 2

2

ThreadAbortException您可以通过调用Thread.ResetAbort来停止重新抛出。

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

于 2013-03-27T18:02:19.263 回答
1

这是我想出的解决方案,效果很好。

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

所以,我们真正需要的是一种优雅的方式来处理这个异常而不关闭应用程序。

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

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

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

在测试中,这工作得很好。我会故意在 DoWork() 线程上调用 Thread.Abort(),并观察 Debug 窗口中的输出行。应用程序将报告线程已中止,然后单例服务正确替换了 QueryRequestor。然后替换能够成功地处理请求。

于 2013-03-27T19:21:17.817 回答