IDisposable + шаблон финализатора - PullRequest
2 голосов
/ 03 января 2012

Глядя на шаблон IDisposable + шаблон Finalizer, я кое-что не понимаю:

public class ComplexCleanupBase : IDisposable
{
    private bool disposed = false; // to detect redundant calls

    public ComplexCleanupBase()
    {
        // allocate resources
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!disposed)
        {
            if (disposing)
            {
                // dispose-only, i.e. non-finalizable logic
            }

            // shared cleanup logic
            disposed = true;
        }
    }

    ~ComplexCleanupBase()
    {
        Dispose(false);
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}

Насколько я понимаю, шаблон должен быть реализован, как описано выше.

1) Вызов Dispose () вызывает GC.SuppressFinalize (this), что означает, что объект не должен быть помещен в очередь финализатора, поскольку он уже правильно расположен?Это помогает быстрее освободить объект?

2) Но что, если я вообще не вызываю Dispose () для этого объекта?В этом случае финализатор должен войти, правильно?Но избавьтесь (ложь);абсолютно ничего не делает (только установка disposed = true).Это предназначено?Такое ощущение, что чего-то не хватает ...

Ответы [ 2 ]

0 голосов
/ 03 января 2012

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

Обратите внимание, что реализация деструктора или переопределение Finalize в производном классе, когда родительский класс не ожидает такогоповедение, может производить Гейзенбагс.Среди прочего, GC может иногда решить, что объект класса был заброшен, вызывая его финализатор / деструктор, даже когда используется объект, на который ссылается поле класса.Например, предположим, что статический класс usbThingie управляет USB-контроллерами с помощью целочисленных дескрипторов, а класс-оболочка usbWrapper делает что-то вроде:

  UInt32 myHandle;
  void sendData(Byte data[])
  {
    UsbThingie.send(myHandle, data[0], data.Length);
  }

Если вызов sendData () является последним, что делается дляэкземпляр usbWrapper до того, как он был оставлен, сборщик мусора мог бы заметить, что после вызова UsbThingie.send () - даже до его возврата - дальнейших ссылок на usbWrapper не будет, итаким образом это может безопасно вызвать финализатор.Если финализатор попытается закрыть канал, на который указывает myHandle, это может нарушить передачу, которая имела место;если usbThingie не является поточно-ориентированным, неизвестно, что может произойти.

0 голосов
/ 03 января 2012

Вопрос 1: Да, если GC.SuppressFinalize не вызывается, объект будет помещен в очередь финализатора И будет перемещаться вверх на поколение (если это еще не поколение 2), что означает, что память для этого объекта не будет освобожденадо следующего прохода GC для нового поколения.

Вопрос 2: Ваш комментарий //shared cleanup logic - это то место, куда пойдет общая логика очистки, это то, что могло бы произойти, кроме установки disposed = true.

Кроме того, если оставить в стороне: если ваша логика удаления должна вызываться только один раз, рассмотрите возможность получения lock, неоспоримая блокировка очень быстра в .Net:

public class ComplexCleanupBase : IDisposable
{
  private volatile bool disposed = false; // to detect redundant calls
  private readonly object _mutex = new object();

  protected virtual void Dispose(bool disposing)
  {
    if (!Monitor.TryEnter(_mutex))
    {
      // We're already being disposed.
      return;
    }

    try
    {
      if (!disposed)
      {
        if (disposing)
        {
            // dispose-only, i.e. non-finalizable logic
        }

        // shared cleanup logic
        disposed = true;
      }
    }
    finally
    {
      Monitor.Exit(_mutex);
    }
  }
  ... other methods unchanged
}
...