4

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

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

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

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

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

6 回答 6

11

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

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

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

于 2008-09-29T23:40:09.213 回答
4

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

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

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

于 2008-09-30T07:03:39.977 回答
3

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

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

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

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

  1. 检测是否调用了 Dispose() 或终结器是否正在执行(不允许在终结器中触摸任何托管资源,在被终结的对象之外);
  2. 使子类能够覆盖 Dispose() 方法。
于 2008-09-30T07:30:46.667 回答
0

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

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

于 2008-09-29T22:35:44.547 回答
0

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

于 2008-09-29T23:10:15.183 回答
0

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

于 2010-12-02T07:35:03.323 回答