Является ли недетерминированное управление ресурсами утечкой?


9

Из того, что я вижу, есть две распространенные формы управления ресурсами: детерминированное уничтожение и явное. Примерами первых могут быть деструкторы и интеллектуальные указатели C ++ или подпрограмма DESTROY в Perl, а вторым примером может служить парадигма Ruby «блоки для управления ресурсами» или интерфейс IDispose .NET.

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

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

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

Другой пример использует блоки Ruby. Два подкласса должны освободить ресурсы, поэтому суперкласс выбирает интерфейс, который использует блок в конструкторе, даже если другие конкретные подклассы могут не нуждаться в этом, поскольку они не требуют специального уничтожения.

Является ли тот случай, что последний утечки детали реализации уничтожения ресурса, а первый нет?

РЕДАКТИРОВАТЬ: Сравнение, скажем, Ruby с Perl может быть более справедливым, так как один имеет детерминистическое разрушение, а другой нет, но они оба сборщик мусора.


5
Я испытываю желание сказать «да», но мне нравится слышать, что другие говорят по этому поводу.
Барт ван Инген Шенау

Уничтожение прозрачного ресурса? Помимо того, что вы должны использовать умные указатели вместо обычных указателей? Я не думаю, что это более прозрачно, чем иметь только один механизм (ссылки) для доступа к объектам (в C ++ у вас есть по крайней мере четыре или пять).
Джорджио

@ Джорджио: «Способы доступа к объекту» довольно расплывчаты. Вы имеете в виду читать или писать? Const / Volatile квалификация? Указатели на самом деле не являются «способом доступа к объекту»; практически любое выражение приводит к объекту, а разыменование указателя не является чем-то особенным.
MSalters

1
@ Джорджио: В этом смысле ООП, вы не можете отправить сообщение на указатель C ++. Вам нужно разыменовать указатель для отправки сообщения (*ptr).Message()или аналогично ptr->Message(). Существует бесконечный набор разрешенных выражений, что ((*ptr))->Messageтакже эквивалентно. Но все они сводятся кexpressionIdentifyingAnObject.Message()
MSalters

1
С пересчетом нужно быть осторожным, избегая кругов. Так что эта абстракция тоже просачивается, просто по-другому.
CodesInChaos

Ответы:


2

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

Явное уничтожение аналогично malloc / free в C со всеми подводными камнями. Может быть, с каким-то синтаксическим сахаром, чтобы он выглядел на основе контекста.

Некоторые из преимуществ прозрачного уничтожения по сравнению с явным: -
тот же шаблон
использования - вы не можете забыть освободить ресурс.
- Очистить детали не засоряют пейзаж в месте использования.


2

Ошибка в абстракции на самом деле не в том, что сборка мусора недетерминирована, а в том, что объекты «заинтересованы» в вещах, на которые они ссылаются, и не интересуются вещами, на которые они не держатся. Ссылки. Чтобы понять почему, рассмотрим сценарий объекта, который поддерживает счетчик того, как часто рисуется конкретный элемент управления. При создании он подписывается на событие рисования элемента управления, а при удалении отписывается. Событие click просто увеличивает поле, а метод getTotalClicks()возвращает значение этого поля.

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

Если, например, метод должен был создать новый объект «счетчик краски», выполнить какое-либо действие с элементом управления, наблюдать, сколько раз элемент управления был перекрашен, а затем отказаться от объекта счетчика краски, объект останется подписанным на событие, даже хотя никому не было бы дела, если бы объект и все ссылки на него просто исчезли. Однако объекты не могут быть собраны, пока не получен сам элемент управления. Если бы метод был вызван много тысяч раз за время существования элемента управления (вероятный сценарий), он мог бы вызвать переполнение памяти, но тот факт, что стоимость N вызовов, вероятно, будет O (N ^ 2) или O (N ^ 3), если обработка подписки не была очень эффективной и большинство операций фактически не включало рисование.

Этот конкретный сценарий может быть реализован путем предоставления элементу управления слабой ссылки на контр-объект, а не сильной. Модель слабой подписки полезна, но не работает в общем случае. Предположим, что вместо того, чтобы хотеть иметь объект, который отслеживает один тип события из одного элемента управления, нужно иметь объект регистрации событий, который контролирует несколько элементов управления, а механизм обработки событий системы был таков, что каждому элементу управления нужна ссылка в другой объект журнала событий. В этом случае объект, связывающий элемент управления с регистратором событий, должен оставаться активным только до тех пор, пока обаконтролируемый элемент управления и регистратор событий остаются полезными. Если ни элемент управления, ни регистратор событий не содержат строгой ссылки на связывающее событие, оно прекратит свое существование, даже если оно все еще «полезно». Если один из них содержит сильное событие, время жизни связывающего объекта может быть бесполезно продлено, даже если другой умирает.

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

Если объекты детерминированы уведомлением, когда никто не заинтересован в них, они смогут использовать эту информацию, чтобы гарантировать, что каждый, кто извлечет выгоду из этих знаний, будет проинформирован. Однако в отсутствие такого уведомления не существует общего способа определить, какие объекты считаются «полезными», если известно только существующее множество ссылок, а не семантическое значение, связанное с этими ссылками. Таким образом, любая модель, которая предполагает, что наличие или отсутствие ссылок достаточно для автоматизированного управления ресурсами, будет обречена, даже если GC сможет мгновенно обнаруживать отказ от объекта.


0

Никакой «деструктор или другой интерфейс, который говорит« этот класс должен быть уничтожен », не является контрактом этого интерфейса. Если вы создадите подтип, который не требует специального уничтожения, я бы склонен считать, что нарушение принципа подстановки Лискова ,

Что касается C ++ и других, то особой разницы нет. C ++ использует этот интерфейс для всех своих объектов. Абстракции не могут вытекать, когда они требуются языком.


4
«Если вы создаете подтип, который не требует специального уничтожения», это не нарушение LSP, поскольку no-op - это действительный особый случай уничтожения. Проблема в том, что вы добавляете требование уничтожения в производный класс.
CodesInChaos

Я запутался здесь. Если нужно добавить специальный код уничтожения в подкласс C ++, он вообще не меняет своих шаблонов использования, потому что он автоматический. Это означает, что суперкласс и подкласс все еще могут использоваться взаимозаменяемо. Но с явным обозначением управления ресурсами подкласс, требующий явного уничтожения, сделает его использование несовместимым с суперклассом, не так ли? (Предполагая, что суперкласс не нуждается в явном уничтожении.)
Луи Джекман

@CodesInChaos - ах да, полагаю, это правда.
Теластин

@ljackman: класс, который требует специального уничтожения, накладывает бремя на любого, кто вызывает его конструктор, чтобы гарантировать его выполнение. Это не создает нарушения LSP, так как DerivedFooThatRequiresSpecialDestructionможет быть создано только кодом, который вызывает new DerivedFooThatRequiresSpecialDestruction(). С другой стороны, фабричный метод, возвращающий DerivedFooThatRequiresSpecialDestructionкод, который не ожидал чего-либо, требующего уничтожения, был бы нарушением LSP.
суперкат

0

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

Необходимость следить за циклами вручную не является ни скрытой, ни прозрачной. Единственным исключением является система подсчета ссылок с языком, который запрещает циклы по дизайну. Эрланг может быть примером такой системы.

Так что оба подхода протекают. Основное отличие состоит в том, что деструкторы распространяются повсеместно в C ++, но IDisposeочень редко встречаются в .NET.


1
За исключением циклов, они чрезвычайно редки и практически никогда не встречаются, за исключением явных циклических структур данных. Основное отличие состоит в том, что деструкторы в C ++ обрабатываются правильно везде, но IDispose редко решает проблему в .NET.
DeadMG

«Кроме циклов чрезвычайно редки». На современных языках? Я бы оспорил эту гипотезу.
Джон Харроп
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.