【问题标题】:Using Dispose on a Singleton to Cleanup Resources在 Singleton 上使用 Dispose 来清理资源
【发布时间】:2010-05-28 17:48:39
【问题描述】:

我的问题可能更多地与语义有关,而不是与IDisposable 的实际使用有关。我正在实现一个单例类,该类负责管理在应用程序执行期间创建的数据库实例。当应用程序关闭时,这个数据库应该被删除。

现在我有这个删除由应用程序在关闭时调用的单例的Cleanup() 方法处理。当我为Cleanup() 编写文档时,我突然意识到我在描述应该使用Dispose() 方法来清理资源。我最初没有实现IDisposable,因为它在我的单例中似乎不合适,因为我不想要任何东西来处理单例本身。目前没有,但将来可能是这个Cleanup() 可能被调用但单例应该仍然存在的原因。我想我可以在 Dispose 方法中包含GC.SuppressFinalize(this); 以使其可行。

因此,我的问题是多方面的:

1) 在单例上实现IDisposable 根本上是个坏主意吗?

2) 我是否只是通过使用 Cleanup() 而不是 Dispose() 来混合语义,并且由于我正在处理资源,所以我真的应该使用 dispose 吗?

3) 将使用GC.SuppressFinalize(this); 实现“Dispose()”,这样我的单例实际上不会被破坏,以防我希望它在调用清理数据库后存活。

【问题讨论】:

    标签: c# singleton dispose


    【解决方案1】:
    1. 为单例实现 IDisposable可能如果您使用 CAS 技术而不是锁来创建单例,那么这是个好主意。像这样。

      if (instance == null) {
          var temp = new Singleton();
          if (Interlocked.CompareExchange(ref instance, temp, null) != null) &&
                  temp is IDisposable) {
              ((IDisposable)temp).Dispose();
          }
      }
      return instance
      

      我们创建了一个临时对象并尝试了原子比较和交换,因此如果这个临时对象实现了 IDisposable 并且没有写入实例的位置,我们需要处置这个临时对象。

      避免锁可能很好,但如果使用一些繁重的逻辑在其构造函数中创建单例的实例,也会有点开销。

    2. 如果您不希望其他代码清理或处置您的对象,请不要为此提供任何机会。但是,可能提供某种 reset() 方法以允许单例重新创建自身,例如,如果您使用惰性初始化。像这样的:

      public static Singletong GetInstance() {
        if (instance == null) {
          instance = new Singleton(); //here we re-evalute cache for example
         }
      return instance
      }
      public static void Reset() {
          instance = null;
      } 
      

    【讨论】:

    • 只是想指出第二个示例不是线程安全的,如果您不需要重新创建单例实例,最好的方法是在声明的位置初始化它如果可能,定义静态变量。
    【解决方案2】:

    简而言之,如果你有一个单例并且你调用 dispose。此后任何对象尝试使用它的任何时候,都将使用处于已处置状态的对象。

    现在将其放入并在应用程序完成后处理对象并不一定是坏事。但是,当您调用它时,您必须小心。如果你真的关心清理并且你只有一个引用,你可以把清理代码放在对象终结器~YourClass 这样它只会被.Net调用确定它不再需要(当应用程序关闭,如果它是一个真正的单例)。

    我只是在这里混合语义吗 有一个 Cleanup() 而不是 Dispose() 并且因为我正在处理 资源我真的应该使用 处理?

    是的,这只是语义。 Dispose 是在程序处理完对象后显示需要清理的东西的标准。

    将实现“Dispose()” GC.SuppressFinalize(this);让它这样 我的单身人士实际上并没有被摧毁 如果我希望它在一个 调用清理数据库。

    不,这意味着当您调用 dispose 方法时,垃圾收集器不会调用对象的自定义终结器。

    【讨论】:

    • 你会在 Applications Exitpoint 上给 Singletons .Dispose() 打电话吗?
    • 请记住,Dispose 与自动调用它的 using 语句相关联。不适用于自定义清理(或任何其他)方法。
    【解决方案3】:

    我同意 Kevin 的回答,但想对此添加一些内容。我对你的说法有点困惑:

    当应用程序关闭时 数据库应该被删除。

    你真的是说删除吗?如被毁?您说的是真实的 (SQL) 数据库吗?

    您必须了解,即使您将清理代码放在终结器或 Application_End 事件 (ASP.NET) 中,也无法保证这些代码会被调用。该过程可以终止,或者计算机断电。在应用程序启动时删除数据库似乎更合理,或者至少在启动时有一个回退机制并进行一些清理。

    虽然终结器是处理资源时进行清理的好地方,但在您的情况下,我们谈论的是应用程序资源。我的意思是资源可能没有绑定到单个对象(您的单例),而是整个应用程序的一部分。这可以得到一些抽象的讨论,也许更多的是一个观点。

    我想说的是,当您将该数据库视为应用程序资源时,您的初始化和清理必须不绑定到对象,而是绑定到应用程序。在 ASP.NET 应用程序中,这将是 Application_Start 和 Application_End (global.asax)。在 Windows 窗体应用程序中,这将是 Program.Main。

    不过,当在终结器上使用这些机制时,您不确定您的清理代码是否会执行。

    【讨论】:

    • 数据库是一个嵌入式数据库,应用程序用来保持它运行的进程的状态。它可以长到相当大。如果由于断电或其他原因而没有被删除,那很好,因为用户可以从文件系统中删除文件。此外,它并不总是被删除,因为用户可以选择在应用程序关闭后保留它。我的单例对象负责创建和删除数据库(在应用程序的整个生命周期中可能有很多),所以我相信它应该负责关闭。
    猜你喜欢
    • 2013-05-12
    • 2023-03-26
    • 2011-02-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多