Вопрос

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

Чтобы убедиться, что поток освобождается, когда использование класса заканчивается, я добавил IDisposable и финализатор, который вызывает Dispose().Предполагая , что stop = true действительно ли это приводит к завершению этого потока, правильно ли это?Это нормально - вызывать Dispose из финализатора, верно?

Финализаторы всегда должны вызывать Dispose если object наследует IDisposable, верно?

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

~BackgroundWorker()
{
    this.Dispose();
}
Это было полезно?

Решение

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

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

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

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

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

Другие советы

Во-первых, 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);
}

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

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

Повторите блокировку - изменчивое поле bool может вызвать меньше проблем.

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top