IDisposable с деструктором: требуется поточно-ориентированная реализация? - PullRequest
3 голосов
/ 17 января 2011

Это только для меня, чтобы убедиться, что я правильно понял:

У нас есть большой класс ресурсов, реализующий шаблон IDisposal.Он должен (по замыслу) быть реализован таким образом, чтобы он вызывался более одного раза (даже если мы попытаемся вызвать его ровно один раз, конечно).Мы также реализуем финализатор, который также вызывает метод Dispose () - как резервную копию.При вызове вручную Dispose () также вызовет GC.SuppressFinalize (this).

Существует несколько примеров схем утилизации.Большинство из них вызывают GC.SuppressFinalize (this) на end кода утилизации.Некоторые утверждают, что было бы лучше вызвать его в начале метода Dispose (), за до любой очистки.Позже я расскажу, что это убедило бы, что GC не вызывает финализатор одновременно, пока мы все еще убираем.

Вопрос:
Кажется, размещение GC.SuppressFinalize в начале не поможет?У нас все еще есть состояние гонки, верно?Так правда ли, что вместо этого мы должны реализовать Dispose () безопасным для потока способом?

Ответы [ 3 ]

4 голосов
/ 17 января 2011

ГХ очищает только те объекты, которые «недоступны».

Класс, в котором выполняется код, все еще «достижим», потому что его указатель this находится в стеке. Таким образом, пока выполняется утилизация, финализатор вызываться не будет.

Так что не имеет значения, если вы позвоните SuppressFinalize в начале или в конце.

Как указано ниже в комментариях, реализация CLR, по-видимому, не гарантирует того, что ваш объект не получит / завершит сборку мусора во время выполнения методов экземпляра. Единственная возможная «надежная» ссылка, поддерживающая работу объекта, - это та, которая используется для вызова метода объекта, но я не знаю достаточно о внутренних элементах JIT, чтобы делать заявления об этом, и его поведение может измениться.

Я оставляю здесь ответ для доступа к обсуждению ниже.

2 голосов
/ 17 января 2011

Несмотря на то, что иногда возможно завершение объекта при наличии, казалось бы, живой ссылки, это может произойти, только когда ничто иное не будет ссылаться на объект. GC.SuppressFinalize (this) активно ссылается на текущий объект 'this', гарантируя, что он не будет завершен до тех пор, пока не будет выполнен GC.SuppressFinalize. Кроме того, тот факт, что ссылка на объект существовала для удаления объекта и была доступна для метода Dispose, гарантирует, что финализатор не мог быть поставлен в очередь до запуска Dispose, если только объект не был мертв и где-то не был завершен или что-то из другого объекта) воскресил его.

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

1 голос
/ 17 января 2011

Утилизация не должна вызывать никаких исключений.

Я хотел бы убедиться, что весь код в Dispose является потокобезопасным, чтобы в случае его вызова он не делал ничего странного. Обычно добавление проверок, если переменные равны нулю, должно сработать.

В примерах Microsoft я видел GC.SuppressFinalize только в конце функции Dispose.

...