C#: есть ли преимущество в удалении ресурсов в порядке, обратном их распределению?

Много лет назад меня увещевали по возможности освобождать ресурсы в порядке, обратном порядку их распределения. То есть:

block1 = malloc( ... );
block2 = malloc( ... );

... do stuff ...

free( block2 );
free( block1 );

Я предполагаю, что на машине MS-DOS с 640 КБ это могло бы минимизировать фрагментацию кучи. Есть ли какое-либо практическое преимущество в том, чтобы делать это в приложении C#/.NET, или это привычка, которая изжила себя?


person Bob Kaufman    schedule 09.11.2009    source источник


Ответы (5)


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

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

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

Вот пример: посмотрите на операцию с базой данных. Вы не хотите закрывать/удалять свое соединение перед закрытием/удалением вашей команды (которая использует соединение).

person Reed Copsey    schedule 09.11.2009
comment
Создано хорошо не хорошо. Он должен зависеть от заказа, выпуска и быть известным во многих обстоятельствах. Это не может иметь большого значения во всех реализациях баз данных, транзакций и всего, что работает в стеке (большинство программного обеспечения). Блокировки — еще один пример, и есть множество не внешних и не бедных библиотек, использующих его. Файловые операции и их блокировки — это другое. Утечка события другая. Любой неуправляемый ресурс, зависящий от еще одного. Созидание и разрушение идут рука об руку, и эту идиому нельзя толковать как Ресурс-Инициализация-Есть-Хорошее-Созидание. - person rama-jka toti; 09.11.2009
comment
Таким образом, оксюморон WC в RIIWC заменен на Aquisition, что, кстати, подразумевает Освобождение. А так как память и большое количество ресурсов в основном абстрагируются, упс, идея идет... и следуют всевозможные взломы. Короче говоря, это просто характер проблемы, и это имеет большое значение. - person rama-jka toti; 09.11.2009
comment
И хотя я не защищаю здесь зависимость от порядка, верное замечание состоит в том, что это чрезвычайно актуально, но редко желательно. Но это то, чем даже официальные спецификации VM крайне ограничены. В особенности это касается Java, а CLR в меньшей, но все же значительной степени. Это хак, чтобы не нарушать большие объемы рабочего кода и сделанные предположения, осознанное решение разработчиков компилятора и jit-бэкенда. Код, способный к независимой от порядка обработки, предоставляет огромное количество возможностей, но может быть неприемлемым для множества сценариев. - person rama-jka toti; 09.11.2009

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

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

person Community    schedule 09.11.2009
comment
Верно, если вы не используете удаленную ссылку случайно (через другой объект, созданный из нее), поскольку вы удаляете в произвольном порядке. - person Reed Copsey; 09.11.2009

Если вы имеете в виду время вызова деструктора объектов, то это сборщик мусора, программирование может иметь очень мало влияния на это, и оно явно недетерминировано в соответствии с определением языка.

Если вы имеете в виду вызов IDisposable.Dispose(), то это зависит от поведения объектов, реализующих интерфейс IDisposable.

Как правило, порядок не имеет значения для большинства объектов Framework, за исключением той степени, в которой он важен для вызывающего кода. Но если объект A поддерживает зависимость от объекта B, а объект B удаляется, то вполне может быть важно не делать определенных вещей с объектом A.

В большинстве случаев Dispose() не вызывается напрямую, а вызывается неявно как часть инструкции using или foreach, и в этом случае естественным образом возникает шаблон обратного порядка в соответствии с встраиванием инструкции.

using(Foo foo = new Foo())
using(FooDoodler fooDoodler = new FooDoodler(foo))
{
  // do stuff
  // ...
  // fooDoodler automatically gets disposed before foo at the end of the using statement.
}
person Jeffrey L Whitledge    schedule 09.11.2009

Вложенные «использования» показывают вам, что «пережившие» на самом деле не включены и редко включаются (не говорить никогда после 40 лет доказательств). И это включает в себя виртуальную машину на основе стека, которая работает, скажем, на CMOS.

[Несмотря на некоторые попытки MSDN.com и Duffius заставить его исчезнуть, вы знаете, что все это для вас, разница между кучей и стеком. Какая умная идея.. в космосе ]

person rama-jka toti    schedule 09.11.2009

«Среда выполнения не дает никаких гарантий относительно порядка вызова методов Finalize. Предположим, например, что есть объект, содержащий указатель на внутренний объект. Сборщик мусора обнаружил, что оба объекта являются мусором. , скажем, что метод Finalize внутреннего объекта вызывается первым. Теперь методу Finalize внешнего объекта разрешено обращаться к внутреннему объекту и вызывать методы для него, но внутренний объект был финализирован, и результаты могут быть непредсказуемыми. По этой причине настоятельно рекомендуется, чтобы методы Finalize не обращались к каким-либо внутренним объектам-членам».

http://msdn.microsoft.com/en-us/magazine/bb985010.aspx

Таким образом, вы можете сколько угодно беспокоиться о своей семантике LIFO dispose, но если вы ее утечете, Dispose() будет вызываться в любом порядке, который выберет CLR.

(Это более или менее то, что Уилл сказал выше)

person piers7    schedule 08.02.2010