【问题标题】:What is the recommended way to guard against resource leaks in the context of ThreadAbortException?在 ThreadAbortException 的上下文中防止资源泄漏的推荐方法是什么?
【发布时间】:2011-06-07 02:37:27
【问题描述】:

我正在努力提高一段代码的异常安全性,我意识到引发的ThreadAbortException 可能会导致不希望的资源泄漏,即使在使用 C# using 构造保护资源时也是如此。例如,考虑以下代码(可能在单独的线程中运行)。

using (TextWriter writer = CreateWriter(filename))
{
    // do something with the writer.
}

TextWriter CreateWriter(string filename)
{
    return new CustomWriter(File.OpenWrite(filename));
}

如果运行此代码的线程异常终止,那么我希望filename 引用的文件句柄立即关闭。我可以在不使用 try/finally 块替换 using 构造的情况下执行此操作吗?

我的假设是ThreadAbortException 可以随时提出,这意味着我应该注意语句之间发生的事情。虽然我可以使用 try/finally 块来防止 CreateWriter 中的异常,但 using 构造在评估括号中的表达式之前不会做同样的事情,这意味着如果发生异常,文件资源将保持打开状态在CreateWriter 返回后立即。

我知道终结器最终会释放文件句柄,但我想知道是否有确定性的方法来解决这个问题,而无需在每个使用 CreateWriter 的地方捕获 ThreadAbortException

【问题讨论】:

    标签: c# .net idisposable using threadabortexception


    【解决方案1】:

    是的,防止这种情况的确定方法是不使用Thread.Abort。曾经。向您的线程发出信号,该停止了,并让它们优雅地终止。 Thread.Abort 是一个伟大的大红鲱鱼,放置在 API 中只是为了绊倒你。 ;)

    http://www.interact-sw.co.uk/iangblog/2004/11/12/cancellation

    【讨论】:

    • 公平地说,MSDN 也警告不要使用它。维护它是为了向后兼容。
    【解决方案2】:

    有一个权衡。

    1. 确保立即关闭所有资源,即使存在 ThreadAbortException 也是如此
    2. 有更简单的代码,但如果调用 Abort() 会暂时泄漏资源

    我假设您没有调用 Abort,只是想要一种在其他人调用时安全的方法。如果您要调用 Abort,那么我建议您不要这样做。这不是您会遇到的唯一问题。还有其他problems with Abort in the documentation

    #2 是一个有效的选择,因为 Abort() 的调用者应该预料到这一点。

    如果您想选择 #1,那么我认为即使是简单的 try/catch 也无济于事。如果 ThreadAbortException 可能在任何地方发生,那么它仍然可能在文件打开后(在 File.OpenWrite() 内部)发生,并且在您将其分配给您可以调用 Dispose() 的变量之前 - 您将遇到同样的问题就像在你的代码中使用一样。

    你需要像这样的语义

      using (var handle = GetUnOpenedHandle()) {
            handle.Open(); // this can't involve assignment to any field of handle
      }
    

    我不确定这是否可行。

    【讨论】:

      【解决方案3】:

      在许多情况下(但绝对不是全部)您可以防范ThreadAbortException。 .NET BCL 中的大多数关键代码已经很好地做到了这一点。问题在于,真的很难做到正确。出于这个原因,大多数人建议并且正确地建议避免中止线程。从 2.0 版开始,CLR 使线程中止变得更加可容忍,并引入了一组新的 API 来帮助代码作者防范它们。请查看Constrained Execution Regions,深入了解所有这些工作原理。

      我相信您对using 块示例的担忧是正确的。要使受约束的执行区域正常工作,必须在try 块内发生带外(异步)异常。但是,由于using 扩展的方式,表达式在try 块之外进行评估。与 lock 块的扩展形成对比,后者从 try 块中评估表达式。嗯,这是真的 with version 4.0 of the framework 无论如何,并且为了防止这些异常进行了专门更改。

      所以问题是为什么没有对using 块进行相同的更改。 According to Joe Duffy 这是一个可以接受的省略,因为假设线程中止应该总是之后是 AppDomain 的终止,无论如何都会触发终结器。

      所以是的。您的代码不能容忍带外(异步)异常。但是,比我聪明的人普遍认为它不应该是这样。

      【讨论】:

        【解决方案4】:

        线程中止最常用于致命错误的情况,因此您的响应应该是让您的应用程序终止。如果您试图完全停止自己的线程,请使用 Thread.Join()。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-02-08
          • 2015-03-28
          • 2015-08-04
          • 1970-01-01
          • 2014-04-28
          • 1970-01-01
          相关资源
          最近更新 更多