2008-09-29 10 views
4

У меня есть класс с именем BackgroundWorker, который имеет поток, постоянно работающий. Чтобы отключить этот поток, переменная экземпляра с именем stop должна быть true.Finalizers и Dispose

Чтобы убедиться, что поток освобожден при использовании класса, я добавил 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(); 
} 
+0

опечатка в первом абзаце? «ложь» должна быть «истинной», верно? – Blorgbeard 2008-09-29 22:39:35

ответ

3

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

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

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

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

  1. обнаруживая ли называется Dispose() или финализации выполняет (вы не можете касаться любых управляемых ресурсов в финализаторе, за пределами объекта, который завершается);
  2. включение подклассов для переопределения метода Dispose().
0

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

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

+0

«Стоп» - это частная переменная. Если вы не можете вызвать this.Dispose() из финализатора, как я могу гарантировать, что объект будет удален, когда объект будет очищен GC? – core 2008-09-29 22:38:34

+0

Я не говорю, что вы не можете вызвать Dispose - я говорю, что если это просто переменная, это не имеет значения - объект все равно исчезает. – 2008-09-30 03:02:05

0

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

11

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

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

Повторите «серьезное предупреждение» - он * не делает блокировку в финализаторе - только в Dispose() - из-за проверки «если (распоряжение)». Тем не менее, летучий дурак, вероятно, будет лучше. Этот код только останавливает работника, когда вызывается Dispose(), что является разумным и допустимым использованием. – 2008-09-30 06:55:46

3

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

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

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

0

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