终结器和处置

时间:2020-03-06 14:54:00  来源:igfitidea点击:

我有一个名为BackgroundWorker的类,该类具有不断运行的线程。要关闭该线程,需要将名为stop的实例变量设置为true。

为了确保在使用完类后释放线程,我添加了IDisposable和终结器调用Dispose()。假设stop = true确实导致该线程退出,此代码段是否正确?从终结器调用Dispose是可以的,对吧?

如果object继承了IDisposable,则终结器应该始终调用Dispose,对吗?

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

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

解决方案

" stop"实例变量是属性吗?如果不是这样,则在终结器期间设置它没有特别的意义,那就是不再引用该对象,因此没有什么可以查询该成员。

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

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

首先,一个严重的警告。不要像我们一样使用终结器。如果我们在终结器中锁定,则可能会设置一些非常不好的效果。短篇小说是不要这样做。现在到原始问题。

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);
}

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

出于兴趣,出于某种原因不能使用常规的BackgroundWorker(完全支持取消),这是什么原因?

重新锁定易失性布尔字段可能不会那么麻烦。

但是,在这种情况下,终结器没有做任何有趣的事情,尤其是在给定" if(dispose)"的情况下,即,它仅在Dispose()期间运行有趣的代码。就我个人而言,我很想坚持使用IDisposable,而不提供终结器:我们应该使用Dispose()对其进行清理。

代码很好,尽管锁定终结器有些"吓人",如果我们遇到死锁,我会避免使用它……我不确定要怎么办,但这并不是很好。但是,如果我们安全的话,这应该不是问题。大多。垃圾收集的内部很痛苦,我希望我们永远不必看到它们;)

正如Marc Gravell指出的那样,挥发性的bool可使我们摆脱锁定,从而减轻了此问题。如果可以,请实施此更改。

nedruod的代码将分配放在if(处置)检查中,这完全是错误的,线程是非托管资源,即使没有显式处置也必须停止。代码很好,我只是指出我们不应该接受该代码片段中给出的建议。

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

  • 检测是否调用了Dispose()或者终结器正在执行(不允许我们在终结器中触摸正在终结的对象之外的终结器中的任何托管资源);
  • 使子类可以重写Dispose()方法。

实现终结器的对象需要引用存储在另一个对象中的标志,线程将能够看到该标志。线程不得对实现终结器的对象有任何直接或者间接的强引用。终结器应使用CompareExchange之类的方法设置标志,并且线程应使用类似的方法对其进行测试。请注意,如果一个对象的终结器访问另一个对象,则另一个对象可能已被终结,但它仍然存在。如果终结器引用其他对象,则这样做的方式不会被它们的终结烦恼,这是很好的。如果我们正在做的只是设置一个标志,那很好。