Название: Manager + shared Отправлено: Igors от Август 18, 2016, 11:06 Добрый день
Вот разбираюсь в обильном чужом коде. Есть система "менеджеров" (читай синглтонов) которые хранят мапы имя->объект одного типа. Пример Код Теперь мы можем брать объект по имени и удалять его Код Подобную организацию я видел не раз, не скажу что это "плохо". НО - тут же начинаются пляски с шаред пойнтером. Вот меня это как-то смутило. Ведь если "менеджер" берет на себя "ownership" то все удаления должны идти через него. С др стороны шаред грохнет если ноль ссылок. Как Вы думаете - могут эти 2 подхода "мирно сосуществовать"? Или это несомненно "косяк архитектуры"? Спасибо Название: Re: Manager + shared Отправлено: Racheengel от Август 18, 2016, 11:13 Если деструктор Skeleton корректно сообщает менеджеру о своей "смерти", то все ок.
Название: Re: Manager + shared Отправлено: ssoft от Август 18, 2016, 11:23 На самом деле не ясно, как внутри менеджера храниться объект. В представленном примере владение уникальное, а все указатели на объект - это всего лишь ссылки без контроля владения.
Если возможен многопоточный доступ к менеджеру, то такой указатель в любой момент может стать невалидным (из-за возможности удаления) и это "косяк архитектуры". Если заменить простые указатели на shared_ptr, то это решит проблему с валидностью объекта, но может создать проблемы (не обязательно) с перекрестными ссылками. Поэтому лучше вместо простых указателей использовать weak_ptr, преобразуя их в shared_ptr во время работы с объектом. В данном случае - если многопоточный доступ отсутствует, то в данных ограничениях решение валидное; если многопоточный доступ присутствует, то имеется "косяк архитектуры". Название: Re: Manager + shared Отправлено: Racheengel от Август 18, 2016, 11:38 В данном случае - если многопоточный доступ отсутствует, то в данных ограничениях решение валидное; если многопоточный доступ присутствует, то имеется "косяк архитектуры". Тогда это будет косяк имплементации, а не архитектуры. С "архитектурой" тут проблем нет. Название: Re: Manager + shared Отправлено: ssoft от Август 18, 2016, 11:41 Тогда это будет косяк имплементации, а не архитектуры. С "архитектурой" тут проблем нет. Согласен ;) Название: Re: Manager + shared Отправлено: Igors от Август 18, 2016, 12:05 "Многопоточность" отсутствует.
Поэтому лучше вместо простых указателей использовать weak_ptr, преобразуя их в shared_ptr во время работы с объектом. Если имеется ввиду "менеджер хранит weak_ptr", то 1) Сейчас менеджер грузит объекты не заботясь о том кто их юзает (может пока и никто). Чем заменить этот ф-ционал если он хранит weak? 2) Что делать с Код ? Через weak_ptr никого не удалить В общем так менеджер теряет ownership и становится чем-то типа "наблюдающего" (пусть и полезного) Название: Re: Manager + shared Отправлено: Igors от Август 18, 2016, 14:15 Если деструктор Skeleton корректно сообщает менеджеру о своей "смерти", то все ок. Житейски разумно, но явно "не айс" (як каже молодь). Чего это "младший" дает указания "менеджеру", вообще чего он знает о существовании менеджера? И это не решает коллизии "менеджер грохает используемого" Название: Re: Manager + shared Отправлено: ssoft от Август 18, 2016, 14:29 Если имеется ввиду "менеджер хранит weak_ptr", то ... Ни в коем случае). Менеджер хранит shared_ptr, а вот предоставляет weak_ptr. Тогда в месте использования можно усилить weak_ptr до shared_ptr и локально контролировать время жизни объекта. Даже если менеджер удалит объект, грохнется он только после удаления последнего shared_ptr. Название: Re: Manager + shared Отправлено: Racheengel от Август 18, 2016, 15:35 Чего это "младший" дает указания "менеджеру", вообще чего он знает о существовании менеджера? Менеджер при создании объекта "говорит" ему, кто хозяин: Skeleton* Manager::AddSkeleton() { Skeleton* slave = new Skeleton(); slave->setParent(this); return slave; } А при смерти slave в этом случае он просто получает информацию о событии: Skeleton::~Skeleton() { if (m_parent) m_parent->slaveKilled(this); } Тогда никаких проблем не возникнет. И это не решает коллизии "менеджер грохает используемого" Тогда менеджер просто получит вызов slaveKilled с поинтером на прибиваемый объект, но он же об этом в курсе :) Название: Re: Manager + shared Отправлено: _Bers от Август 18, 2016, 22:03 Добрый день Как Вы думаете - могут эти 2 подхода "мирно сосуществовать"? Или это несомненно "косяк архитектуры"? да. могут. shared_ptr позволяет гибкие стратегии владения ресурсами. Название: Re: Manager + shared Отправлено: _Bers от Август 18, 2016, 22:04 Даже если менеджер удалит объект, грохнется он только после удаления последнего shared_ptr. не факт. зависит от бизнес-логики менеджера. Название: Re: Manager + shared Отправлено: Igors от Август 19, 2016, 06:11 да. могут. К слову: а по-моему с гибкостью у него проблемы. Напр я рассчитывал на удаление объекта, но по каким-то причинам его нет. Как узнать "кто держит"? shared_ptr позволяет гибкие стратегии владения ресурсами. Ну ладно, так что - менеджер должен хранить (вместо обычных указателей как сейчас) Код Тогда все что попало в менеджер остается неубиенным. А если менеджер удалит свой шаред из мапы - еще хуже: значит объект уже загружен, а в менеджере его нет - действующий через менеджер вероятно загрузит еще копию. Где же (гибкая) стратегия владения? Название: Re: Manager + shared Отправлено: Igors от Август 20, 2016, 09:30 И это не решает коллизии "менеджер грохает используемого" Тогда менеджер просто получит вызов slaveKilled с поинтером на прибиваемый объект, но он же об этом в курсе :) Менеджер при создании объекта "говорит" ему, кто хозяин: Есть одна система управления/ownership (менеджер). Вы предлагаете добавить еще другую (parent-child).. Это будет работать только в случае m_parent == manager. Тогда зачем заводить m_parent, slaveKilled и.т.п. если можно действовать через тот же синглтонSkeleton* Manager::AddSkeleton() { Skeleton* slave = new Skeleton(); slave->setParent(this); return slave; } А при смерти slave в этом случае он просто получает информацию о событии: Skeleton::~Skeleton() { if (m_parent) m_parent->slaveKilled(this); } Тогда никаких проблем не возникнет. Код
Название: Re: Manager + shared Отправлено: Авварон от Август 20, 2016, 09:46 Где же (гибкая) стратегия владения? shared+weak в манагере. Код: { Название: Re: Manager + shared Отправлено: _Bers от Август 20, 2016, 14:33 да. могут. К слову: а по-моему с гибкостью у него проблемы. Напр я рассчитывал на удаление объекта, но по каким-то причинам его нет. Как узнать "кто держит"? shared_ptr позволяет гибкие стратегии владения ресурсами. Ну ладно, так что - менеджер должен хранить (вместо обычных указателей как сейчас) Код Тогда все что попало в менеджер остается неубиенным. А если менеджер удалит свой шаред из мапы - еще хуже: значит объект уже загружен, а в менеджере его нет - действующий через менеджер вероятно загрузит еще копию. Где же (гибкая) стратегия владения? ничего не понял. что значит "рассчитывал на удаление объекта" ? http://www.cyberforum.ru/cpp-beginners/thread1500101.html#post7882976 Название: Re: Manager + shared Отправлено: Racheengel от Август 20, 2016, 21:43 Цитировать Есть одна система управления/ownership (менеджер). Вы предлагаете добавить еще другую (parent-child).. Это будет работать только в случае m_parent == manager. Тогда зачем заводить m_parent, slaveKilled и.т.п. если можно действовать через тот же синглтон Это не другая система, а гарантия того, что будет корректно работать "убивание" объектов не через менеджер. Естественно, m_parent == manager - это основное для этого условие. m_parent не должен быть указателем на класс менеджера, он может просто указывать на интерфейс типа IManager, у которого есть метод типа IManager::OnObjectDestroyed(IObject* obj). Тогда никакой синглтон не нужен. Тем более, в этом случае менеджеров может быть и несколько, не обязательно один :) Название: Re: Manager + shared Отправлено: Igors от Август 21, 2016, 08:09 Ага, все получается, причем совсем несложно
Код AddSkeleton - клиент отвечает за создание объекта и уникальность ключа, обязан что-то делать если AddSkeleton вернул false (напр попробовать с др ключом/именем) DeleteSkeleton - обнуляет shared, но используемый объект остается в мапе и доступен через GetSkeleton до тех пор пока все клиенты его не освободят GetSkeleton - возвращает weak, клиент решает делать ли его shared (захватывать ли ресурс) Кстати необязательно плодить менеджеров, достаточно одного на все типы, надо в качестве ключа взять пару typeid(T).name() + string. Ну это может и не очень хорошо т.к. напр SkeletonManager имеет ф-ционал специфичный для Skeleton Название: Re: Manager + shared Отправлено: ssoft от Август 21, 2016, 16:59 Нет, не так shared и weak работают - они работают в связке. weak автоматически обнуляется, если ни одного shared больше нет.
Должно быть что-то типа этого (написал прямо здесь, возможны синтаксические ошибки) Код
А еще лучше, чтобы избежать возможности явной работы с указателем на Skeleton. Код
Работа в программе будет выглядеть как Код
Название: Re: Manager + shared Отправлено: Igors от Август 22, 2016, 03:19 Нет, не так shared и weak работают - они работают в связке. weak автоматически обнуляется, если ни одного shared больше нет. Не понял - Вы же сами (совершенно верно) рекомендовали возвращать weak, Я так и сделал - удаление обнуляет shared, но weak может еще житьДолжно быть что-то типа этого (написал прямо здесь, возможны синтаксические ошибки) Без weak мы ничего не достигаем - мапа не хранит объекты которые могут существоватьА еще лучше, чтобы избежать возможности явной работы с указателем на Skeleton. Не раз задумывался над подобным. С одной стороны да, хочется обеспечить чтобы все железно шло только через менеджера - иначе всегда остается вероятность ошибки/просмотра. Но на практике это часто неудобно. Тот же Skeleton - конструктор создаст его "пустым", потом его надо грузить (напр из файла), и необязательно он успешно загрузится. Потом апдейтить и.т.п. Тогда чего совать "неконсистентный" объект в глобальную мапу? А в multithreaded (пусть его здесь и нет) это уже прямая ошибка.Код
Название: Re: Manager + shared Отправлено: ssoft от Август 22, 2016, 15:09 Manager хранит shared, но возвращает то weak при вызове GetSkeleton. Сформированный weak и хранимый shared взаимосвязаны (взаимопреобразуемы) - как только shared будет удален (обнулен) все связанные с ним weak автоматически обнулятся. Так как shared и weak взаимопреобразуемы, то нет необходимости в одновременном хранении shared и weak.
В стандарте С++11 и выше можно использовать переменное количество аргументов шаблона для вызова произвольного конструктора Skeleton Код: template < typename _Skeleton, typename ... _Arguments > Название: Re: Manager + shared Отправлено: Igors от Август 22, 2016, 16:18 Manager хранит shared, но возвращает то weak при вызове GetSkeleton. Сформированный weak и хранимый shared взаимосвязаны (взаимопреобразуемы) - как только shared будет удален (обнулен) все связанные с ним weak автоматически обнулятся. Так как shared и weak взаимопреобразуемы, то нет необходимости в одновременном хранении shared и weak. Есть. Пример- клиент получает объект (GetSkeleton) и делает его shared, - клиент удаляет объект из мапы (DeleteSkeleton), но это не ведет к уничтожению объекта - клиент опять запрашивает GetSkeleton, в мапе его уже нет (в Вашем сценарии), получив NULL клиент вероятно создает новый объект, загружает его с диска и добавляет в менеджер Итог: менеджер позволяет одновременное существование одинаковых объектов (или по крайней мере с 1 ключом) Название: Re: Manager + shared Отправлено: ssoft от Август 22, 2016, 21:13 Если клиент удаляет объект из мапы (DeleteSkeleton), то это ведет к моментальному или отложенному уничтожению объекта. Отложенное удаление возникает только в случае, если какой-либо клиент(ы) временно преобразовал weak указатель в shared для возможности использования. Как только все shared удаляются, уничтожается и объект (в традиционном случае). Наличие weak указателей на уничтожение объекта никак не влияет.
Фактически возможно кратковременное существование двух и даже более подобных объектов - это плата за отсутствие синхронизации. Без явной синхронизации достигается так называемая "согласованность в конечном счете". Если требуется строгое наличие только одного экземпляра, то необходимо применять методы явной синхронизации уже с их недостатками (блокировать работу с менеджером до полного удаления скелетона). В любом случае необходимо защитить мапу от одновременного доступа Mutex или ReadWriteLocker. Могу отметить, что оба подхода в экстремальной ситуации (перегрузка процессора) ведут себя по разному плохо. Однако согласованность в конечном счете лучше параллелизуется. Но если есть другие ограничения, например по оперативной или графической памяти, то второй вариант может быть и лучше. Здесь все зависит от конкретных условий функционирования ПО. Название: Re: Manager + shared Отправлено: Igors от Август 23, 2016, 09:31 Фактически возможно кратковременное существование двух и даже более подобных объектов - это плата за отсутствие синхронизации. В данном случае этой платы можно легко избежать. К тому же будет ли это существование кратко (или долго) временным - решает клиент. В любом случае необходимо защитить мапу от одновременного доступа Mutex или ReadWriteLocker. Наверное атомарный мутекс здесь бы лучше подошел. Multi-threaded нет и в обозримом будущем не предвидится. Есть десятка 2 "контроллеров", некоторые из них сложные, но многим нужны те же скелетоны и др ресурсы. Использование менеджеров мне кажется вполне оправданным |