【问题标题】:Finalizers and Dispose终结器和处置
【发布时间】:2008-09-29 22:28:27
【问题描述】:

我有一个名为BackgroundWorker 的类,它有一个不断运行的线程。要关闭此线程,名为stop 的实例变量需要为true

为了确保在使用完类时释放线程,我添加了IDisposable 和一个调用Dispose() 的终结器。假设 stop = true 确实导致该线程退出,那么这个 sippet 是否正确?从终结器调用Dispose 没问题,对吧?

如果object 继承IDisposable,终结者应该总是调用Dispose,对吧?

/// <summary>
/// Force the background thread to exit.
/// </summary>
public void Dispose()
{
    lock (this.locker)
    {
        this.stop = true;
    }
}

~BackgroundWorker()
{
    this.Dispose();
}

【问题讨论】:

  • 第一段有错字? “假”应该是“真”,对吧?

标签: c# dispose idisposable finalizer disposable


【解决方案1】:

首先,严重警告。不要像你一样使用终结器。如果你在终结器中使用锁,你就会为自己设置一些非常糟糕的效果。短篇小说是不要这样做。现在回到最初的问题。

public void Dispose()
{
    Dispose(true);
    GC.SuppressFinalize(this);
}

/// <summary>
/// Force the background thread to exit.
/// </summary>
protected virtual void Dispose(bool disposing)
{
    if (disposing)
    {
        lock (this.locker)
        {
            this.stop = true;
        }
    }
}

~BackgroundWorker()
{
    Dispose(false);
}

拥有终结器的唯一原因是允许子类扩展和释放非托管资源。如果您没有子类,请密封您的类并完全删除终结器。

【讨论】:

  • 重新发出“严重警告”——他没有锁定终结器——仅在 Dispose() 中——由于“if(disposing)”检查.也就是说,volatile bool 可能会更好。此代码仅在调用 Dispose() 时停止工作程序,这是合理有效的使用。
【解决方案2】:

出于兴趣,有什么理由不能使用完全支持取消的常规BackgroundWorker

重新锁定 - volatile bool 字段可能不那么麻烦。

然而,在这种情况下,你的终结器并没有做任何有趣的事情,尤其是考虑到“if(disposing)”——即它只在 Dispose() 期间运行有趣的代码。就我个人而言,我倾向于只使用 IDisposable,而不提供终结器:您应该使用 Dispose() 对其进行清理。

【讨论】:

    【解决方案3】:

    您的代码很好,虽然锁定在终结器中有点“可怕”,我会避免它 - 如果你遇到死锁......我不能 100% 确定会发生什么,但它不会很好。但是,如果您是安全的,这应该不是问题。大多。垃圾收集的内部是痛苦的,我希望你永远不必看到它们;)

    正如 Marc Gravell 指出的那样,volatile bool 可以让您摆脱锁,从而缓解这个问题。如果可以,请实施此更改。

    nedruod 的代码将赋值放在 if (disposing) 检查中,这是完全错误的 - 线程是非托管资源,即使没有显式处理也必须停止。您的代码很好,我只是指出您不应该接受该代码 sn-p 中给出的建议。

    是的,如果实现 IDisposable 模式,您几乎总是应该从终结器调用 Dispose()。完整的 IDisposable 模式比你拥有的要大一些,但你并不总是需要它——它只是提供了两种额外的可能性:

    1. 检测是否调用了 Dispose() 或正在执行终结器(不允许在终结器中触摸任何托管资源,在被终结的对象之外);
    2. 允许子类覆盖 Dispose() 方法。

    【讨论】:

    • 确实,终结器中的锁很可怕,因此也不鼓励! MSDN link: 因为只有一个线程可以运行所有的终结器,如果一个终结器被阻塞,其他终结器就无法运行。因此,不鼓励锁定终结器。
    【解决方案4】:

    “停止”实例变量是属性吗?如果没有,则在终结器期间设置它没有特别的意义 - 不再引用该对象,因此没有任何内容可以查询该成员。

    如果您实际上是在释放资源,那么让 Dispose() 和终结器执行相同的工作(首先测试该工作是否仍需要完成)是一个很好的模式。

    【讨论】:

    • "stop" 是一个私有变量。如果无法从finalizer中调用this.Dispose(),如何保证GC清理对象时对象被dispose?
    • 我不是说你不能调用 Dispose - 我是说如果它只是一个变量就没有关系 - 反正对象正在消失。
    【解决方案5】:

    您需要完整的一次性模式,但停止必须是线程可以访问的东西。如果它是被处置的类的成员变量,那就不好了,因为它不能引用已处置的类。考虑让线程拥有一个事件并在处置时发出信号。

    【讨论】:

      【解决方案6】:

      实现终结器的对象需要对标志的引用——存储在另一个对象中——线程将能够看到该标志;线程必须对实现终结器的对象有任何直接或间接的强引用。终结器应该使用类似 CompareExchange 的东西设置标志,并且线程应该使用类似的方法来测试它。请注意,如果一个对象的终结器访问另一个对象,则另一个对象可能已经终结,但它仍然存在。如果终结器引用其他对象的方式不会被它们的终结所困扰,那么终结器就可以了。如果你所做的只是设置一个标志,那么你很好。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-04-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-03-13
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多