Как правильно использовать пулы памяти в стиле С++ 11?

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

Я планирую использовать эту реализацию пула памяти, которая удовлетворяет требованиям Allocator. Для автоматического уничтожения объектов-оболочек я планирую использовать std::shared_ptr, поэтому, когда все компоненты освобождают обертку, она автоматически уничтожается, а использованная память возвращается в пул.

Чего я не понимаю, так это того, как эти две концепции могут сойтись. Если я выделю память из пула напрямую (вызвав allocate()), это даст мне указатель на блок данных, что нормально, но как тогда deallocate() будет вызываться автоматически? Или мне нужно использовать другой контейнер для моих объектов-оболочек, таких как std::list, и передать ему распределитель пула памяти?


person Szabolcs Szekelyi    schedule 02.03.2020    source источник


Ответы (1)


Вы можете использовать std::shared_ptr с настраиваемым распределителем, используя std::allocate_shared. Это, вероятно, то, что вы хотите в любом случае, поскольку я предполагаю, что вы хотите, чтобы управляющий блок (т.е. счетчики ссылок) также распределялся с использованием распределителя пула.

При создании объекта с использованием std::allocate_shared копия распределителя сохраняется внутри shared_ptr, поэтому при уничтожении будет вызываться правильный deallocate().

Обратите внимание, что вы также можете создать свой std::shared_ptr с помощью специального удаления, например:

auto allocator = getAllocator<Foo>();
auto ptr = std::shared_ptr<Foo>(new(allocator.allocate()) Foo,
  [&allocator](auto * ptr) { allocator.deallocate(ptr); });

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

Кстати, если вы все еще «присматриваетесь», вот еще одна реализация пула памяти, которая мне очень нравится: foonathan: :память. Он предоставляет собственный allocate_shared.

person AVH    schedule 02.03.2020
comment
Спасибо за ответ. В этом есть одна особенность: слоты в пуле памяти имеют фиксированный размер, который должен быть известен во время компиляции. Как мне это рассчитать? Я знаю размер объектов, которые я хочу выделить, то же самое для распределителя (необходимого из-за копии), но как насчет блока управления? - person Szabolcs Szekelyi; 03.03.2020
comment
Хорошие вопросы и одна вещь, которая мне нравится в библиотеке foonathan::memory, поскольку в ней есть множество вспомогательных функций, помогающих именно в таких вещах. Я бегло взглянул на API shared_prt, но не нашел ничего, что позволило бы вам определить размер управляющего блока... - person AVH; 03.03.2020