Финализаторы и утилизация - PullRequest
       55

Финализаторы и утилизация

4 голосов
/ 30 сентября 2008

У меня есть класс с именем BackgroundWorker, в котором постоянно работает поток. Чтобы отключить этот поток, переменная экземпляра с именем stop должна быть 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();
}

Ответы [ 6 ]

11 голосов
/ 30 сентября 2008

Во-первых, строгое предупреждение . Не используйте финализатор, как вы. Вы настраиваете себя на очень плохие эффекты, если вы берете блокировки в финализаторе. Короче говоря, не делай этого. Теперь к первоначальному вопросу.

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

Единственная причина иметь финализатор вообще - это позволить подклассам расширять и освобождать неуправляемые ресурсы . Если у вас нет подклассов, закройте свой класс и полностью отбросьте финализатор.

3 голосов
/ 30 сентября 2008

Ваш код в порядке, хотя блокировка в финализаторе несколько "страшна", и я бы этого не допустил - если вы зашли в тупик ... Я не уверен на 100%, что произойдет, но это не будет хорошо. Однако, если вы в безопасности, это не должно быть проблемой. В основном. Внутренние элементы сборки мусора болезненны, и я надеюсь, что вам никогда не придется их видеть;)

Как отмечает Марк Гравелл, изменчивый бул позволит вам избавиться от блокировки, что уменьшит эту проблему. Реализуйте это изменение, если можете.

Код nedruod помещает назначение в проверку if (распоряжение), что совершенно неверно - поток является неуправляемым ресурсом и должен быть остановлен, даже если он не был явно уничтожен. Ваш код в порядке, я просто указываю, что вы не должны следовать советам, приведенным в этом фрагменте кода.

Да, вам почти всегда следует вызывать Dispose () из финализатора при реализации шаблона IDisposable. Полный шаблон IDisposable немного больше, чем у вас, но он вам не всегда нужен - он просто предоставляет две дополнительные возможности:

  1. определение того, был ли вызван Dispose () или выполняется финализатор (вам не разрешается касаться каких-либо управляемых ресурсов в финализаторе, за исключением завершаемого объекта);
  2. включение подклассов для переопределения метода Dispose ().
3 голосов
/ 30 сентября 2008

Из интереса, по какой причине это не может использовать обычный BackgroundWorker , который имеет полную поддержку отмены?

Re the lock - изменчивое поле bool может быть менее хлопотным.

Тем не менее, в этом случае ваш финализатор не делает ничего интересного, особенно с учетом «если (удаление)» - т.е. он выполняет только интересный код во время Dispose (). Лично у меня будет соблазн придерживаться только IDisposable и не предоставлять финализатор: вы должны очистить его с помощью Dispose ().

0 голосов
/ 02 декабря 2010

Объект, который реализует финализатор, нуждается в ссылке на флаг - сохраненный в другом объекте - который поток сможет увидеть; поток должен не иметь прямую или косвенную ссылку на объект, который реализует финализатор. Финализатор должен установить флаг, используя что-то вроде CompareExchange, а поток должен использовать аналогичные средства для его проверки. Обратите внимание, что если финализатор одного объекта обращается к другому объекту, другой объект, возможно, был завершен, но он все еще будет существовать. Финализатор может ссылаться на другие объекты, если он делает это так, чтобы их финализация не беспокоила. Если все, что вы делаете, это устанавливаете флаг, у вас все в порядке.

0 голосов
/ 30 сентября 2008

Вам нужен полный одноразовый шаблон, но стоп должен быть тем, к чему может получить доступ нить. Если это переменная-член располагаемого класса, это нехорошо, потому что она не может ссылаться на удаленный класс. Подумайте о том, чтобы иметь событие, которым владеет поток, и вместо этого сигнализировать о его удалении.

0 голосов
/ 30 сентября 2008

Является ли переменная экземпляра "stop" свойством? Если нет, то нет особого смысла устанавливать его во время финализатора - ничто больше не ссылается на объект, поэтому ничто не может запросить член.

Если вы на самом деле освобождаете ресурс, тогда выполнение Dispose () и финализатор выполняют одну и ту же работу (первое тестирование, нужно ли еще выполнять эту работу), - хороший пример.

...