【问题标题】:How to recover after ThreadAbortException?ThreadAbortException 后如何恢复?
【发布时间】:2013-03-27 17:51:16
【问题描述】:

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

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

处理此问题的最佳方法是什么?我们该如何优雅地处理这个问题?

【问题讨论】:

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


【解决方案1】:

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

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

【讨论】:

  • 我不想停止重新投掷。我只想优雅地处理它。在发布这个问题后不久,我想出了一些东西。我得把答案提出来。
【解决方案2】:

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

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

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

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

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

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

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

【讨论】:

    猜你喜欢
    • 2021-04-17
    • 1970-01-01
    • 1970-01-01
    • 2011-12-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-30
    • 2013-12-28
    相关资源
    最近更新 更多