Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения

Allocators, containers and memory allocation algorithms

Boost , The Boost C++ Libraries BoostBook Documentation Subset , Chapter 16. Boost.Interprocess

Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

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

Boost.Interprocess STL compatible allocators are configurable via template parameters. Allocators define their pointer typedef based on the void_pointer typedef of the segment manager passed as template argument. When this segment_manager::void_pointer is a relative pointer, (for example, offset_ptr<void>) the user can place these allocators in memory mapped in different base addresses in several processes.

Распределители контейнеров обычно могут быть построены по умолчанию, потому что они не имеют состояния.<std::allocatorBoost.Pool's<boost::pool_allocator>/<boost::fast_pool_allocator>являются примерами дефолтно-конструктивных распределителей.

С другой стороны,Boost.Interprocessраспределители должны выделять память из конкретного сегмента памяти, а не из общесистемного источника памяти (например, кучи).Boost.Interprocessallocators arestateful, что означает, что они должны быть сконфигурированы так, чтобы сообщать им, где находится общая память или картированный файл памяти.

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

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

//The allocators must be templatized with the segment manager type
typedef any_interprocess_allocator
   <int, managed_shared_memory::segment_manager, ...> Allocator;
//The allocator must be constructed with a pointer to the segment manager
Allocator alloc_instance (segment.get_segment_manager(), ...);
//Containers must be initialized with a configured allocator
typedef my_list<int, Allocator> MyIntList;
MyIntList mylist(alloc_inst);
//This would lead to a compilation error, because
//the allocator has no default constructor
//MyIntList mylist;

Boost.InterprocessAllocators также имеют функцию<get_segment_manager()>, которая возвращает базовый менеджер сегмента, который они получили в конструкторе:

Allocator::segment_manager s = alloc_instance.get_segment_manager();
AnotherType *a = s->construct<AnotherType>(anonymous_instance)(/*Parameters*/);

При замене контейнеров STL активно обсуждается, что делать с распределителями. Некоторые реализации STL, например Dinkumware от Visual. NET 2003, выполняет глубокий своп всего контейнера через временный, когда распределители не равны.предложенная резолюцияо замене контейнеров заключается в том, что распределители должны быть заменены небросовым способом.

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

Пока не будет достигнуто окончательное решение.Boost.InterprocessАллокаторы реализуют функцию неброскового свопа, которая меняет внутренние указатели. Если распределитель, помещенный в сегмент общей памяти, обменивается с другим, помещенным в другой сегмент общей памяти, результат не определен. Но авария вполне уверена.

Класс<allocator>определяет класс распределителя, который использует алгоритм управляемого сегмента памяти для распределения и распределения памяти. Это достигается черезменеджер сегментовсегмента управляемой памяти. Этот распределитель является эквивалентом для управляемых сегментов памяти стандарта<std::allocator>.<allocator>Темплатируется с выделенным типом и диспетчером сегментов.

Равенство:Два<allocator>экземпляра, построенные с одним и тем же менеджером сегмента, равны. Если экземпляр создается с помощью конструктора копий, этот экземпляр сравнивается с оригиналом.

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

Для использования<allocator>вы должны включить следующий заголовок:

#include <boost/interprocess/allocators/allocator.hpp>

<allocator>имеет следующее заявление:

namespace boost {
namespace interprocess {
template<class T, class SegmentManager>
class allocator;
}  //namespace interprocess {
}  //namespace boost {

Аллокатор просто предоставляет необходимые типдефы и пересылает все запросы на распределение и распределение сделок менеджеру сегмента, прошедшему в конструкторе, точно так же, как<std::allocator>пересылает запросы на<operatornew[]>.

<allocator>Это очень просто:

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <cassert>
using namespace boost::interprocess;
int main ()
{
   //Remove shared memory on construction and destruction
   struct shm_remove
   {
      shm_remove() { shared_memory_object::remove("MySharedMemory"); }
      ~shm_remove(){ shared_memory_object::remove("MySharedMemory"); }
   } remover;
   //Create shared memory
   managed_shared_memory segment(create_only,
                                 "MySharedMemory",  //segment name
                                 65536);
   //Create an allocator that allocates ints from the managed segment
   allocator<int, managed_shared_memory::segment_manager>
      allocator_instance(segment.get_segment_manager());
   //Copy constructed allocator is equal
   allocator<int, managed_shared_memory::segment_manager>
      allocator_instance2(allocator_instance);
   assert(allocator_instance2 == allocator_instance);
   //Allocate and deallocate memory for 100 ints
   allocator_instance2.deallocate(allocator_instance.allocate(100), 100);
   return 0;
}

Алгоритмы памяти переменного размера тратят некоторое пространство в управленческой информации для каждого распределения. Иногда, как правило, для небольших объектов это неприемлемо. Алгоритмы памяти также могут фрагментировать сегмент управляемой памяти по некоторым схемам распределения и распределения, снижая их производительность. При распределении многих объектов одного и того же типа простое сегрегированное хранилище становится быстрым и удобным для использования в космосе распределителем, как объясняется в.Boost.Poolбиблиотека.

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

Boost.Interprocess offers 3 allocators based on this segregated storage algorithm: node_allocator, private_node_allocator and cached_node_allocator.

Чтобы узнать подробности реализации отдельных пулов хранения см.ОсуществлениеBoost.InterprocessSegregated storage poolssection.

<node_allocator>,<private_node_allocator>и<cached_node_allocator>реализуют стандартный интерфейс распределителя и функции, описанные вСвойства увеличения. Межпроцессные распределители.

Все эти распределители упрощаются по 3 параметрам:

  • <classT>: Тип, который будет выделен.
  • <classSegmentManager>: Тип менеджера сегмента, который будет передан в конструкторе.
  • <std::size_tNodesPerChunk>: Количество узлов, которые будет содержать кусок памяти. Это значение будет определять размер памяти, которую пул запросит менеджеру сегмента, когда у пула заканчиваются узлы. Этот параметр имеет значение по умолчанию.

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

Для распределителей узлов с кучной памятью (например,Boost.Pool's<boost::fast_pool_allocator>обычно используется глобальный, нитевидный монотонный пул для каждого размера узла. Это невозможно, если попытаться разделить распределитель узлов между процессами. Для достижения этого совместного использования<node_allocator>используется уникальная служба распределения типов менеджера сегмента (см.Уникальная конструкция экземпляра).

При инициализации объект<node_allocator>ищет этот уникальный объект в сегменте. Если он не установлен, он строит один. Таким образом, все 96 объектов, встроенных в сегмент памяти, имеют уникальный пул памяти.

Общее сегрегированное хранилище делится не только между узлами_аллокаторами одного типа, но и между всеми распределителями узлов, которые выделяют объекты одинакового размера, например,node_allocatorиnode_allocator. Это экономит много памяти, но также накладывает накладные расходы на синхронизацию для каждого распределения узлов.

Динамически созданное общее сегрегированное хранилище интегрирует исходное число так, что<node_allocator>может знать, прикреплен ли какой-либо другой<node_allocator>к тому же общему сегрегированному хранилищу. Когда последний распределитель, прикрепленный к бассейну, разрушается, бассейн разрушается.

Равенство:Два<node_allocator>экземпляра, построенные с одним и тем же менеджером сегмента, сравниваются равными. Если экземпляр создается с помощью конструктора копий, этот экземпляр сравнивается с оригиналом.

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

Чтобы использовать<node_allocator>, вы должны включить следующий заголовок:

#include <boost/interprocess/allocators/node_allocator.hpp>

<node_allocator>имеет следующее заявление:

namespace boost {
namespace interprocess {
template<class T, class SegmentManager, std::size_t NodesPerChunk = ...>
class node_allocator;
}  //namespace interprocess {
}  //namespace boost {

Пример использования<node_allocator>:

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/allocators/node_allocator.hpp>
#include <cassert>
using namespace boost::interprocess;
int main ()
{
   //Remove shared memory on construction and destruction
   struct shm_remove
   {
      shm_remove() { shared_memory_object::remove("MySharedMemory"); }
      ~shm_remove(){ shared_memory_object::remove("MySharedMemory"); }
   } remover;
   //Create shared memory
   managed_shared_memory segment(create_only,
                                 "MySharedMemory",  //segment name
                                 65536);
   //Create a node_allocator that allocates ints from the managed segment
   //The number of chunks per segment is the default value
   typedef node_allocator<int, managed_shared_memory::segment_manager>
      node_allocator_t;
   node_allocator_t allocator_instance(segment.get_segment_manager());
   //Create another node_allocator. Since the segment manager address
   //is the same, this node_allocator will be
   //attached to the same pool so "allocator_instance2" can deallocate
   //nodes allocated by "allocator_instance"
   node_allocator_t allocator_instance2(segment.get_segment_manager());
   //Create another node_allocator using copy-constructor. This
   //node_allocator will also be attached to the same pool
   node_allocator_t allocator_instance3(allocator_instance2);
   //All allocators are equal
   assert(allocator_instance == allocator_instance2);
   assert(allocator_instance2 == allocator_instance3);
   //So memory allocated with one can be deallocated with another
   allocator_instance2.deallocate(allocator_instance.allocate(1), 1);
   allocator_instance3.deallocate(allocator_instance2.allocate(1), 1);
   //The common pool will be destroyed here, since no allocator is
   //attached to the pool
   return 0;
}

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

Такprivate_node_allocatorиспользует то же сегрегированное хранилище, что и<node_allocator>, но каждыйprivate_node_allocatorимеет свой собственный сегрегированный пул хранения. Никакая синхронизация не используется при распределении узлов, поэтому для операции, которая обычно включает в себя только несколько операций указателя при распределении и размещении узла, гораздо меньше накладных расходов.

Равенство:Два<private_node_allocator>экземпляраникогдане сравниваются равными. Память, выделенная одним распределителем, не можетиметь дело с другим.

Безопасность потоков распределения:Распределение и распределение сделокнебезопасны для потоков.

Чтобы использовать<private_node_allocator>, вы должны включить следующий заголовок:

#include <boost/interprocess/allocators/private_node_allocator.hpp>

<private_node_allocator>имеет следующее заявление:

namespace boost {
namespace interprocess {
template<class T, class SegmentManager, std::size_t NodesPerChunk = ...>
class private_node_allocator;
}  //namespace interprocess {
}  //namespace boost {

Пример использования<private_node_allocator>:

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/allocators/private_node_allocator.hpp>
#include <cassert>
using namespace boost::interprocess;
int main ()
{
   //Remove shared memory on construction and destruction
   struct shm_remove
   {
      shm_remove() { shared_memory_object::remove("MySharedMemory"); }
      ~shm_remove(){ shared_memory_object::remove("MySharedMemory"); }
   }  remover;
   //Create shared memory
   managed_shared_memory segment(create_only,
                                 "MySharedMemory",  //segment name
                                 65536);
   //Create a private_node_allocator that allocates ints from the managed segment
   //The number of chunks per segment is the default value
   typedef private_node_allocator<int, managed_shared_memory::segment_manager>
      private_node_allocator_t;
   private_node_allocator_t allocator_instance(segment.get_segment_manager());
   //Create another private_node_allocator.
   private_node_allocator_t allocator_instance2(segment.get_segment_manager());
   //Although the segment manager address
   //is the same, this private_node_allocator will have its own pool so
   //"allocator_instance2" CAN'T deallocate nodes allocated by "allocator_instance".
   //"allocator_instance2" is NOT equal to "allocator_instance"
   assert(allocator_instance != allocator_instance2);
   //Create another node_allocator using copy-constructor.
   private_node_allocator_t allocator_instance3(allocator_instance2);
   //This allocator is also unequal to allocator_instance2
   assert(allocator_instance2 != allocator_instance3);
   //Pools are destroyed with the allocators
   return 0;
}

Общее совместное использование узлов<node_allocator>может налагать высокие накладные расходы на некоторые приложения, а минимальные накладные расходы на синхронизацию<private_node_allocator>могут налагать неприемлемые потери памяти для других приложений.

Для решения этой проблемыBoost.Interprocessпредлагает распределитель<cached_node_allocator>, который выделяет узлы из общего пула, но кэширует некоторые из них в частном порядке, так что последующие распределения не имеют накладных расходов на синхронизацию. Когда кэш заполнен, распределитель возвращает некоторые кэшированные узлы в общий пул, и они будут доступны другим распределителям.

Равенство:Два<cached_node_allocator>экземпляра, построенные с одним и тем же менеджером сегмента, сравниваются равными. Если экземпляр создается с помощью конструктора копий, этот экземпляр сравнивается с оригиналом.

Безопасность потоков распределения:Распределение и распределение сделокнебезопасны для потоков.

Чтобы использовать<cached_node_allocator>, вы должны включить следующий заголовок:

#include <boost/interprocess/allocators/cached_node_allocator.hpp>

<cached_node_allocator>имеет следующее заявление:

namespace boost {
namespace interprocess {
template<class T, class SegmentManager, std::size_t NodesPerChunk = ...>
class cached_node_allocator;
}  //namespace interprocess {
}  //namespace boost {

Пример<cached_node_allocator>и экземпляр<node_allocator>имеют один и тот же пул, если оба экземпляра получают одинаковые параметры шаблона. Это означает, что узлы, возвращенные в общий пул одним из них, могут быть повторно использованы другим. Обратите внимание, что это не означает, что оба распределителя равны, это просто информация для программистов, которые хотят максимально использовать пул.

<cached_node_allocator>, предлагает дополнительные функции для управления кэшем (кэш может управляться, например):

  • <voidset_max_cached_nodes(std::size_t n)>: Установите максимальный предел кэшированных узлов. Если кэшированные узлы достигают предела, некоторые возвращаются в общий пул.
  • <std::size_tget_max_cached_nodes()const>: возвращает максимальный предел кэшированных узлов.
  • <voiddeallocate_cache()>: Возвращает кэшированные узлы в общий бассейн.

Пример использования<cached_node_allocator>:

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/allocators/cached_node_allocator.hpp>
#include <cassert>
using namespace boost::interprocess;
int main ()
{
   //Remove shared memory on construction and destruction
   struct shm_remove
   {
      shm_remove() { shared_memory_object::remove("MySharedMemory"); }
      ~shm_remove(){ shared_memory_object::remove("MySharedMemory"); }
   } remover;
   //Create shared memory
   managed_shared_memory segment(create_only,
                                 "MySharedMemory",  //segment name
                                 65536);
   //Create a cached_node_allocator that allocates ints from the managed segment
   //The number of chunks per segment is the default value
   typedef cached_node_allocator<int, managed_shared_memory::segment_manager>
      cached_node_allocator_t;
   cached_node_allocator_t allocator_instance(segment.get_segment_manager());
   //The max cached nodes are configurable per instance
   allocator_instance.set_max_cached_nodes(3);
   //Create another cached_node_allocator. Since the segment manager address
   //is the same, this cached_node_allocator will be
   //attached to the same pool so "allocator_instance2" can deallocate
   //nodes allocated by "allocator_instance"
   cached_node_allocator_t allocator_instance2(segment.get_segment_manager());
   //The max cached nodes are configurable per instance
   allocator_instance2.set_max_cached_nodes(5);
   //Create another cached_node_allocator using copy-constructor. This
   //cached_node_allocator will also be attached to the same pool
   cached_node_allocator_t allocator_instance3(allocator_instance2);
   //We can clear the cache
   allocator_instance3.deallocate_cache();
   //All allocators are equal
   assert(allocator_instance == allocator_instance2);
   assert(allocator_instance2 == allocator_instance3);
   //So memory allocated with one can be deallocated with another
   allocator_instance2.deallocate(allocator_instance.allocate(1), 1);
   allocator_instance3.deallocate(allocator_instance2.allocate(1), 1);
   //The common pool will be destroyed here, since no allocator is
   //attached to the pool
   return 0;
}

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

Такое поведение может быть проблематичным, если несколько контейнеров используют<boost::interprocess::node_allocator>для временного распределения большого количества объектов, но они заканчивают хранение нескольких из них: пул узлов будет заполнен узлами, которые не будут повторно использоваться, теряя память из сегмента.

Адаптивные распределители на основе пула обмениваются некоторым пространством (накладные расходы могут составлять до 1%) и производительностью (приемлемой для многих приложений) с возможностью возврата свободных кусков узлов в сегмент памяти, чтобы их можно было использовать любым другим контейнером или управляемым объектом. Чтобы узнать подробности реализации «адаптивных пулов» см.РеализацияBoost.Intrusiveадаптивных пуловраздел.

Как с выделенными распределителями узлов на основе хранения, Boost. Интерпроцесс предлагает 3 новых распределителя:<adaptive_pool>,<private_adaptive_pool>,<cached_adaptive_pool>.

<adaptive_pool>,<private_adaptive_pool>и<cached_adaptive_pool>реализуют стандартный интерфейс распределителя и функции, описанные вСвойства увеличения. Межпроцессные распределители.

Все эти распределители упрощаются по 4 параметрам:

  • <classT>: Тип, который будет выделен.
  • <classSegmentManager>: Тип менеджера сегмента, который будет передан в конструкторе.
  • <std::size_tNodesPerChunk>: Количество узлов, которые будет содержать кусок памяти. Это значение будет определять размер памяти, которую пул запросит менеджеру сегмента, когда у пула заканчиваются узлы. Этот параметр имеет значение по умолчанию.
  • <std::size_tMaxFreeChunks>: Максимальное количество свободных кусков, которые будет держать бассейн. Если этот предел достигнут, пул возвращает куски менеджеру сегмента. Этот параметр имеет значение по умолчанию.

Эти распределители также предлагают функцию<deallocate_free_chunks()>. Эта функция будет проходить через все куски памяти пула и возвращать в управляемый сегмент памяти свободные куски памяти. Эта функция намного быстрее, чем для сегрегированных распределителей памяти, потому что адаптивный алгоритм пула предлагает постоянный доступ к бесплатным кускам.

Так же, как<node_allocator>, для каждого размера узла используется глобальный пул технологических потоков. При инициализации<adaptive_pool>осуществляет поиск пула в сегменте. Если он не установлен, он строит один. Адаптивный пул создается с использованием уникального названия. Адаптивный пул также делится между всеми узлами_аллокаторами, которые выделяют объекты одинакового размера, например,Adaptive_poolиAdaptive_pool.

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

Равенство:Два<adaptive_pool>экземпляра, построенные с одним и тем же менеджером сегмента, сравниваются равными. Если экземпляр создается с помощью конструктора копий, этот экземпляр сравнивается с оригиналом.

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

Чтобы использовать<adaptive_pool>, вы должны включить следующий заголовок:

#include <boost/interprocess/allocators/adaptive_pool.hpp>

<adaptive_pool>имеет следующее заявление:

namespace boost {
namespace interprocess {
template<class T, class SegmentManager, std::size_t NodesPerChunk = ..., std::size_t MaxFreeChunks = ...>
class adaptive_pool;
}  //namespace interprocess {
}  //namespace boost {

Пример использования<adaptive_pool>:

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/allocators/adaptive_pool.hpp>
#include <cassert>
using namespace boost::interprocess;
int main ()
{
   //Remove shared memory on construction and destruction
   struct shm_remove
   {
      shm_remove() { shared_memory_object::remove("MySharedMemory"); }
      ~shm_remove(){ shared_memory_object::remove("MySharedMemory"); }
   } remover;
   //Create shared memory
   managed_shared_memory segment(create_only,
                                 "MySharedMemory",  //segment name
                                 65536);
   //Create a adaptive_pool that allocates ints from the managed segment
   //The number of chunks per segment is the default value
   typedef adaptive_pool<int, managed_shared_memory::segment_manager>
      adaptive_pool_t;
   adaptive_pool_t allocator_instance(segment.get_segment_manager());
   //Create another adaptive_pool. Since the segment manager address
   //is the same, this adaptive_pool will be
   //attached to the same pool so "allocator_instance2" can deallocate
   //nodes allocated by "allocator_instance"
   adaptive_pool_t allocator_instance2(segment.get_segment_manager());
   //Create another adaptive_pool using copy-constructor. This
   //adaptive_pool will also be attached to the same pool
   adaptive_pool_t allocator_instance3(allocator_instance2);
   //All allocators are equal
   assert(allocator_instance == allocator_instance2);
   assert(allocator_instance2 == allocator_instance3);
   //So memory allocated with one can be deallocated with another
   allocator_instance2.deallocate(allocator_instance.allocate(1), 1);
   allocator_instance3.deallocate(allocator_instance2.allocate(1), 1);
   //The common pool will be destroyed here, since no allocator is
   //attached to the pool
   return 0;
}

Так же, как<private_node_allocator>владеет частным сегрегированным хранилищем,<private_adaptive_pool>владеет собственным адаптивным бассейном. Если пользователь хочет избежать чрезмерного распределения узлов, синхронизация накладных расходов в контейнере<private_adaptive_pool>является хорошим выбором.

Равенство:Два<private_adaptive_pool>экземпляраникогдане сравниваются равными. Память, выделенная одним распределителем, не можетиметь дело с другим.

Безопасность потоков распределения:Распределение и распределение сделокнебезопасны для потоков.

Чтобы использовать<private_adaptive_pool>, вы должны включить следующий заголовок:

#include <boost/interprocess/allocators/private_adaptive_pool.hpp>

<private_adaptive_pool>имеет следующее заявление:

namespace boost {
namespace interprocess {
template<class T, class SegmentManager, std::size_t NodesPerChunk = ..., std::size_t MaxFreeChunks = ...>
class private_adaptive_pool;
}  //namespace interprocess {
}  //namespace boost {

Пример использования<private_adaptive_pool>:

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/allocators/private_adaptive_pool.hpp>
#include <cassert>
using namespace boost::interprocess;
int main ()
{
   //Remove shared memory on construction and destruction
   struct shm_remove
   {
      shm_remove() { shared_memory_object::remove("MySharedMemory"); }
      ~shm_remove(){ shared_memory_object::remove("MySharedMemory"); }
   } remover;
   //Create shared memory
   managed_shared_memory segment(create_only,
                                 "MySharedMemory",  //segment name
                                 65536);
   //Create a private_adaptive_pool that allocates ints from the managed segment
   //The number of chunks per segment is the default value
   typedef private_adaptive_pool<int, managed_shared_memory::segment_manager>
         private_adaptive_pool_t;
   private_adaptive_pool_t allocator_instance(segment.get_segment_manager());
   //Create another private_adaptive_pool.
   private_adaptive_pool_t allocator_instance2(segment.get_segment_manager());
   //Although the segment manager address
   //is the same, this private_adaptive_pool will have its own pool so
   //"allocator_instance2" CAN'T deallocate nodes allocated by "allocator_instance".
   //"allocator_instance2" is NOT equal to "allocator_instance"
   assert(allocator_instance != allocator_instance2);
   //Create another adaptive_pool using copy-constructor.
   private_adaptive_pool_t allocator_instance3(allocator_instance2);
   //This allocator is also unequal to allocator_instance2
   assert(allocator_instance2 != allocator_instance3);
   //Pools are destroyed with the allocators
   return 0;
}

Адаптивные пулы также имеют кэшированную версию. В этом распределителе распределитель кэширует некоторые узлы, чтобы избежать синхронизации и бухгалтерских накладных расходов общего адаптивного пула.<cached_adaptive_pool>выделяет узлы из общего адаптивного пула, но кэширует некоторые из них в частном порядке, чтобы последующие распределения не имели накладных расходов на синхронизацию. Когда кэш заполнен, распределитель возвращает некоторые кэшированные узлы в общий пул, и они будут доступны другим<cached_adaptive_pools>или<adaptive_pools>того же управляемого сегмента.

Равенство:Два<cached_adaptive_pool>экземпляра, построенные с одним и тем же менеджером сегмента, сравниваются равными. Если экземпляр создается с помощью конструктора копий, этот экземпляр сравнивается с оригиналом.

Безопасность потоков распределения:Распределение и распределение сделокнебезопасны для потоков.

Чтобы использовать<cached_adaptive_pool>, вы должны включить следующий заголовок:

#include <boost/interprocess/allocators/cached_adaptive_pool.hpp>

<cached_adaptive_pool>имеет следующее заявление:

namespace boost {
namespace interprocess {
template<class T, class SegmentManager, std::size_t NodesPerChunk = ..., std::size_t MaxFreeNodes = ...>
class cached_adaptive_pool;
}  //namespace interprocess {
}  //namespace boost {

Пример<cached_adaptive_pool>и экземпляр<adaptive_pool>имеют один и тот же пул, если оба экземпляра получают одинаковые параметры шаблона. Это означает, что узлы, возвращенные в общий пул одним из них, могут быть повторно использованы другим. Обратите внимание, что это не означает, что оба распределителя равны, это просто информация для программистов, которые хотят максимально использовать пул.

<cached_adaptive_pool>, предлагает дополнительные функции для управления кэшем (кэш может управляться, например):

  • <voidset_max_cached_nodes(std::size_t n)>: Установите максимальный предел кэшированных узлов. Если кэшированные узлы достигают предела, некоторые возвращаются в общий пул.
  • <std::size_tget_max_cached_nodes()const>: возвращает максимальный предел кэшированных узлов.
  • <voiddeallocate_cache()>: Возвращает кэшированные узлы в общий бассейн.

Пример использования<cached_adaptive_pool>:

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/allocators/cached_adaptive_pool.hpp>
#include <cassert>
using namespace boost::interprocess;
int main ()
{
   //Remove shared memory on construction and destruction
   struct shm_remove
   {
      shm_remove() { shared_memory_object::remove("MySharedMemory"); }
      ~shm_remove(){ shared_memory_object::remove("MySharedMemory"); }
   } remover;
   //Create shared memory
   managed_shared_memory segment(create_only,
                                 "MySharedMemory",  //segment name
                                 65536);
   //Create a cached_adaptive_pool that allocates ints from the managed segment
   //The number of chunks per segment is the default value
   typedef cached_adaptive_pool<int, managed_shared_memory::segment_manager>
      cached_adaptive_pool_t;
   cached_adaptive_pool_t allocator_instance(segment.get_segment_manager());
   //The max cached nodes are configurable per instance
   allocator_instance.set_max_cached_nodes(3);
   //Create another cached_adaptive_pool. Since the segment manager address
   //is the same, this cached_adaptive_pool will be
   //attached to the same pool so "allocator_instance2" can deallocate
   //nodes allocated by "allocator_instance"
   cached_adaptive_pool_t allocator_instance2(segment.get_segment_manager());
   //The max cached nodes are configurable per instance
   allocator_instance2.set_max_cached_nodes(5);
   //Create another cached_adaptive_pool using copy-constructor. This
   //cached_adaptive_pool will also be attached to the same pool
   cached_adaptive_pool_t allocator_instance3(allocator_instance2);
   //We can clear the cache
   allocator_instance3.deallocate_cache();
   //All allocators are equal
   assert(allocator_instance == allocator_instance2);
   assert(allocator_instance2 == allocator_instance3);
   //So memory allocated with one can be deallocated with another
   allocator_instance2.deallocate(allocator_instance.allocate(1), 1);
   allocator_instance3.deallocate(allocator_instance2.allocate(1), 1);
   //The common pool will be destroyed here, since no allocator is
   //attached to the pool
   return 0;
}

Boost.InterprocessSTL-совместимые распределители предлагают интерфейс STL-совместимых распределителей, и если они определяют свой внутреннийуказательтипдеф в качестве относительного указателя, они могут использоваться для размещения STL-контейнеров в общей памяти, картированных файлах памяти или в сегменте памяти, определенном пользователем.

Однако, как упоминает Скотт Мейерс в своей книге «Эффективный STL», пункт 10,«Будьте в курсе соглашений и ограничений распределителей»:

  • «Стандарт явно позволяет реализаторам библиотеки предположить, что указатель каждого распределителя типизированный индекс является синонимом T*»
  • «Стандарт говорит, что реализация STL позволяет предположить, что все объекты распределения одного и того же типа эквивалентны и всегда сравниваются равными»

Очевидно, что если какая-либо реализация STL игнорирует указатели типа, ни один умный указатель не может использоваться в качестве распределителя::pointer. Если реализации STL предполагают, что все объекты-распределители одного и того же типа сравниваются равными, то предполагается, что два распределителя, каждый из которых выделяется из другого пула памяти, равны, что является полной катастрофой.

Контейнеры STL, которые мы хотим разместить в общей памяти или картированных файлах памяти сBoost.Interprocessне могут сделать ни одно из этих предположений, поэтому:

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

К сожалению, во многих реализациях STL используются необработанные указатели для внутренних данных и игнорируются аллокаторы указателей типа и другие предполагают, что в какой-то момент аллокатором::typedef является T*. Это связано с тем, что на практике не было необходимости в распределителях с указателем типа DEF, отличным от T*, для распределения памяти в пуле / узле.

Пока реализация STL не обрабатывает распределитель::pointer typedefs в общем виде,Boost.Interprocessпредлагает следующие классы:

  • boost:interprocess::vector— реализация<std::vector>, готовая к использованию в управляемых сегментах памяти, таких как общая память. Чтобы использовать его включают:
#include <boost/interprocess/containers/vector.hpp>
  • boost:interprocess:::deque— это реализация<std::deque>, готовая к использованию в управляемых сегментах памяти, таких как общая память. Чтобы использовать его включают:
#include <boost/interprocess/containers/deque.hpp>
  • <list>— это реализация<std::list>, готовая к использованию в управляемых сегментах памяти, таких как общая память. Чтобы использовать его включают:
#include <boost/interprocess/containers/list.hpp>
  • <slist>- это реализация контейнера<slist>SGI, готового к использованию в сегментах управляемой памяти, таких как общая память. Чтобы использовать его включают:
#include <boost/interprocess/containers/slist.hpp>
  • <set>/<multiset>/<map>/<multimap>семейство представляет собой реализацию семейства std::set/multiset/map/multimap, готового к использованию в управляемых сегментах памяти, таких как общая память. К их использованию относятся:
#include <boost/interprocess/containers/set.hpp>
#include <boost/interprocess/containers/map.hpp>
  • <flat_set>/<flat_multiset>/<flat_map>/<flat_multimap>классы являются адаптацией и расширением знаменитого класса AssocVector Андрея Александреску из библиотеки Локи, готовые к совместной памяти. Эти классы предлагают ту же функциональность, что и<std::set/multiset/map/multimap>, реализованные с упорядоченным вектором, который имеет более быстрый поиск, чем стандартные упорядоченные ассоциативные контейнеры на основе красно-черных деревьев, но более медленные вставки. Чтобы использовать его включают:
#include <boost/interprocess/containers/flat_set.hpp>
#include <boost/interprocess/containers/flat_map.hpp>
  • <basic_string>— это реализация<std::basic_string>, готовая к использованию в управляемых сегментах памяти, таких как общая память. Он реализован с использованием векторного непрерывного хранилища, поэтому он имеет быстрое преобразование строк c и может использоваться с классами форматированиявекторного потокаiostream. Чтобы использовать его включают:
#include <boost/interprocess/containers/string.hpp>

Все эти контейнеры имеют те же аргументы по умолчанию, что и стандартные контейнеры, и их можно использовать с другими, неBoost.Interprocessраспределителями (std::allocator, или boost::pool_allocator, например).

Чтобы разместить любой из этих контейнеров в сегментах управляемой памяти, мы должны определить параметр шаблона распределителя с помощью.Boost.Interprocessраспределитель так, что контейнер выделяет значения в сегменте управляемой памяти. Чтобы разместить сам контейнер в общей памяти, мы строим его в сегменте управляемой памяти, как и любой другой объект сBoost.Interprocess:

#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
int main ()
{
   using namespace boost::interprocess;
   //Remove shared memory on construction and destruction
   struct shm_remove
   {
      shm_remove() { shared_memory_object::remove("MySharedMemory"); }
      ~shm_remove(){ shared_memory_object::remove("MySharedMemory"); }
   } remover;
   //A managed shared memory where we can construct objects
   //associated with a c-string
   managed_shared_memory segment(create_only,
                                 "MySharedMemory",  //segment name
                                 65536);
   //Alias an STL-like allocator of ints that allocates ints from the segment
   typedef allocator<int, managed_shared_memory::segment_manager>
      ShmemAllocator;
   //Alias a vector that uses the previous STL-like allocator
   typedef vector<int, ShmemAllocator> MyVector;
   int initVal[]        = {0, 1, 2, 3, 4, 5, 6 };
   const int *begVal    = initVal;
   const int *endVal    = initVal + sizeof(initVal)/sizeof(initVal[0]);
   //Initialize the STL-like allocator
   const ShmemAllocator alloc_inst (segment.get_segment_manager());
   //Construct the vector in the shared memory segment with the STL-like allocator
   //from a range of iterators
   MyVector *myvector =
      segment.construct<MyVector>
         ("MyVector")/*object name*/
         (begVal     /*first ctor parameter*/,
         endVal     /*second ctor parameter*/,
         alloc_inst /*third ctor parameter*/);
   //Use vector as your want
   std::sort(myvector->rbegin(), myvector->rend());
   // . . .
   //When done, destroy and delete vector from the segment
   segment.destroy<MyVector>("MyVector");
   return 0;
}

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

Boost.Interprocessконтейнеры помещаются в файлы, отображаемые в общей памяти/памяти, и т. д. ... с использованием двух механизмоводновременно:

  • Boost.Interprocess<construct<>>,<find_or_construct<>>... функции. Эти функции помещают объект C++ в файл с общей памятью / памятью. Но это помещает только объект, нонепамять, которую этот объект может распределять динамически.
  • Распределение общей памяти. Они позволяют распределять общие части файлов, отображаемые в памяти/памяти, так что контейнеры могут распределять динамические фрагменты памяти для хранения вновь вставленных элементов.

Это означает, что для размещения любогоBoost.Interprocessконтейнера (включаяBoost.Interprocessстрок) в общей памяти или картированных файлах памяти контейнерыдолжны:

  • Определите их шаблонный параметр распределителя наBoost.Interprocessallocator.
  • Каждый конструктор контейнеров должен взять.Усиление.Интерпроцессраспределитель как параметр.
  • Вы должны использовать функции build<>/find_or_construct<>... для размещения контейнера в управляемой памяти.

Если вы делаете первые два пункта, но не используете<construct<>>или<find_or_construct<>>, вы создаете контейнер, размещенныйтольков вашем процессе, но который выделяет память для содержащихся типов из общего файла памяти / памяти.

Давайте посмотрим пример:

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
int main ()
{
   using namespace boost::interprocess;
   //Typedefs
   typedef allocator<char, managed_shared_memory::segment_manager>
      CharAllocator;
   typedef basic_string<char, std::char_traits<char>, CharAllocator>
      MyShmString;
   typedef allocator<MyShmString, managed_shared_memory::segment_manager>
      StringAllocator;
   typedef vector<MyShmString, StringAllocator>
      MyShmStringVector;
   //Open shared memory
   //Remove shared memory on construction and destruction
   struct shm_remove
   {
      shm_remove() { shared_memory_object::remove("MySharedMemory"); }
      ~shm_remove(){ shared_memory_object::remove("MySharedMemory"); }
   } remover;
   managed_shared_memory shm(create_only, "MySharedMemory", 10000);
   //Create allocators
   CharAllocator     charallocator  (shm.get_segment_manager());
   StringAllocator   stringallocator(shm.get_segment_manager());
   //This string is in only in this process (the pointer pointing to the
   //buffer that will hold the text is not in shared memory).
   //But the buffer that will hold "this is my text" is allocated from
   //shared memory
   MyShmString mystring(charallocator);
   mystring = "this is my text";
   //This vector is only in this process (the pointer pointing to the
   //buffer that will hold the MyShmString-s is not in shared memory).
   //But the buffer that will hold 10 MyShmString-s is allocated from
   //shared memory using StringAllocator. Since strings use a shared
   //memory allocator (CharAllocator) the 10 buffers that hold
   //"this is my text" text are also in shared memory.
   MyShmStringVector myvector(stringallocator);
   myvector.insert(myvector.begin(), 10, mystring);
   //This vector is fully constructed in shared memory. All pointers
   //buffers are constructed in the same shared memory segment
   //This vector can be safely accessed from other processes.
   MyShmStringVector *myshmvector =
      shm.construct<MyShmStringVector>("myshmvector")(stringallocator);
   myshmvector->insert(myshmvector->begin(), 10, mystring);
   //Destroy vector. This will free all strings that the vector contains
   shm.destroy_ptr(myshmvector);
   return 0;
}

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

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

Для переноса содержимого контейнера на другой используйте функцию<boost::move()>, как показано в примере. Более подробную информацию о функциях, поддерживающих подвижную семантику, см. в справочном разделе Boost. Межпроцессные контейнеры:

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <cassert>
int main ()
{
   using namespace boost::interprocess;
   //Typedefs
   typedef managed_shared_memory::segment_manager     SegmentManager;
   typedef allocator<char, SegmentManager>            CharAllocator;
   typedef basic_string<char, std::char_traits<char>
                        ,CharAllocator>                MyShmString;
   typedef allocator<MyShmString, SegmentManager>     StringAllocator;
   typedef vector<MyShmString, StringAllocator>       MyShmStringVector;
   //Remove shared memory on construction and destruction
   struct shm_remove
   {
      shm_remove() { shared_memory_object::remove("MySharedMemory"); }
      ~shm_remove(){ shared_memory_object::remove("MySharedMemory"); }
   } remover;
   managed_shared_memory shm(create_only, "MySharedMemory", 10000);
   //Create allocators
   CharAllocator     charallocator  (shm.get_segment_manager());
   StringAllocator   stringallocator(shm.get_segment_manager());
   //Create a vector of strings in shared memory.
   MyShmStringVector *myshmvector =
      shm.construct<MyShmStringVector>("myshmvector")(stringallocator);
   //Insert 50 strings in shared memory. The strings will be allocated
   //only once and no string copy-constructor will be called when inserting
   //strings, leading to a great performance.
   MyShmString string_to_compare(charallocator);
   string_to_compare = "this is a long, long, long, long, long, long, string...";
   myshmvector->reserve(50);
   for(int i = 0; i < 50; ++i){
      MyShmString move_me(string_to_compare);
      //In the following line, no string copy-constructor will be called.
      //"move_me"'s contents will be transferred to the string created in
      //the vector
      myshmvector->push_back(boost::move(move_me));
      //The source string is in default constructed state
      assert(move_me.empty());
      //The newly created string will be equal to the "move_me"'s old contents
      assert(myshmvector->back() == string_to_compare);
   }
   //Now erase a string...
   myshmvector->pop_back();
   //...And insert one in the first position.
   //No string copy-constructor or assignments will be called, but
   //move constructors and move-assignments. No memory allocation
   //function will be called in this operations!!
   myshmvector->insert(myshmvector->begin(), boost::move(string_to_compare));
   //Destroy vector. This will free all strings that the vector contains
   shm.destroy_ptr(myshmvector);
   return 0;
}

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

Вот пример, который создает карту в общей памяти. Ключ — это строка, а нанесённый на карту тип — это класс, который хранит несколько контейнеров:

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/map.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/containers/string.hpp>
using namespace boost::interprocess;
//Typedefs of allocators and containers
typedef managed_shared_memory::segment_manager                       segment_manager_t;
typedef allocator<void, segment_manager_t>                           void_allocator;
typedef allocator<int, segment_manager_t>                            int_allocator;
typedef vector<int, int_allocator>                                   int_vector;
typedef allocator<int_vector, segment_manager_t>                     int_vector_allocator;
typedef vector<int_vector, int_vector_allocator>                     int_vector_vector;
typedef allocator<char, segment_manager_t>                           char_allocator;
typedef basic_string<char, std::char_traits<char>, char_allocator>   char_string;
class complex_data
{
   int               id_;
   char_string       char_string_;
   int_vector_vector int_vector_vector_;
   public:
   //Since void_allocator is convertible to any other allocator<T>, we can simplify
   //the initialization taking just one allocator for all inner containers.
   complex_data(int id, const char *name, const void_allocator &void_alloc)
      : id_(id), char_string_(name, void_alloc), int_vector_vector_(void_alloc)
   {}
   //Other members...
};
//Definition of the map holding a string as key and complex_data as mapped type
typedef std::pair<const char_string, complex_data>                      map_value_type;
typedef std::pair<char_string, complex_data>                            movable_to_map_value_type;
typedef allocator<map_value_type, segment_manager_t>                    map_value_type_allocator;
typedef map< char_string, complex_data
           , std::less<char_string>, map_value_type_allocator>          complex_map_type;
int main ()
{
   //Remove shared memory on construction and destruction
   struct shm_remove
   {
      shm_remove() { shared_memory_object::remove("MySharedMemory"); }
      ~shm_remove(){ shared_memory_object::remove("MySharedMemory"); }
   } remover;
   //Create shared memory
   managed_shared_memory segment(create_only,"MySharedMemory", 65536);
   //An allocator convertible to any allocator<T, segment_manager_t> type
   void_allocator alloc_inst (segment.get_segment_manager());
   //Construct the shared memory map and fill it
   complex_map_type *mymap = segment.construct<complex_map_type>
      //(object name), (first ctor parameter, second ctor parameter)
         ("MyMap")(std::less<char_string>(), alloc_inst);
   for(int i = 0; i < 100; ++i){
      //Both key(string) and value(complex_data) need an allocator in their constructors
      char_string  key_object(alloc_inst);
      complex_data mapped_object(i, "default_name", alloc_inst);
      map_value_type value(key_object, mapped_object);
      //Modify values and insert them in the map
      mymap->insert(value);
   }
   return 0;
}

Как уже упоминалось, разработчикам контейнеров может потребоваться изменить их реализацию, чтобы сделать их совместимыми с Boost. Интерпроцесс, потому что реализация обычно игнорирует распределители с умными указателями. Мы надеемся, что несколько контейнеров Boost совместимы с.Интерпроцесс.

Boost.Unorderedконтейнеры совместимы с Interprocess, поэтому программисты могут хранить хеш-контейнеры в общей памяти и картированных файлах памяти. Вот небольшой пример хранения<unordered_map>в общей памяти:

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/unordered_map.hpp>     //boost::unordered_map
#include <functional>                  //std::equal_to
#include <boost/functional/hash.hpp>   //boost::hash
int main ()
{
   using namespace boost::interprocess;
   //Remove shared memory on construction and destruction
   struct shm_remove
   {
      shm_remove() { shared_memory_object::remove("MySharedMemory"); }
      ~shm_remove(){ shared_memory_object::remove("MySharedMemory"); }
   } remover;
   //Create shared memory
   managed_shared_memory segment(create_only, "MySharedMemory", 65536);
   //Note that unordered_map<Key, MappedType>'s value_type is std::pair<const Key, MappedType>,
   //so the allocator must allocate that pair.
   typedef int    KeyType;
   typedef float  MappedType;
   typedef std::pair<const int, float> ValueType;
   //Typedef the allocator
   typedef allocator<ValueType, managed_shared_memory::segment_manager> ShmemAllocator;
   //Alias an unordered_map of ints that uses the previous STL-like allocator.
   typedef boost::unordered_map
      < KeyType               , MappedType
      , boost::hash<KeyType>  ,std::equal_to<KeyType>
      , ShmemAllocator>
   MyHashMap;
   //Construct a shared memory hash map.
   //Note that the first parameter is the initial bucket count and
   //after that, the hash function, the equality function and the allocator
   MyHashMap *myhashmap = segment.construct<MyHashMap>("MyHashMap")  //object name
      ( 3, boost::hash<int>(), std::equal_to<int>()                  //
      , segment.get_allocator<ValueType>());                         //allocator instance
   //Insert data in the hash map
   for(int i = 0; i < 100; ++i){
      myhashmap->insert(ValueType(i, (float)i));
   }
   return 0;
}

Широко используемая библиотекаBoost.MultiIndexсовместима с.Boost.Interprocess, чтобы мы могли создавать довольно хорошие базы данных в общей памяти. Создание баз данных в общей памяти немного сложнее, чем в обычной памяти, потому что эти базы данных содержат строки, и эти строки должны быть размещены в общей памяти. Общие строки памяти требуют выделения в своих конструкторах, поэтому это обычно усложняет вставку объекта.

Вот пример, который показывает, как поместить контейнер с несколькими индексами в общую память:

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/multi_index_container.hpp>
#include <boost/multi_index/member.hpp>
#include <boost/multi_index/ordered_index.hpp>
using namespace boost::interprocess;
namespace bmi = boost::multi_index;
typedef managed_shared_memory::allocator<char>::type              char_allocator;
typedef basic_string<char, std::char_traits<char>, char_allocator>shm_string;
//Data to insert in shared memory
struct employee
{
   int         id;
   int         age;
   shm_string  name;
   employee( int id_
           , int age_
           , const char *name_
           , const char_allocator &a)
      : id(id_), age(age_), name(name_, a)
   {}
};
//Tags
struct id{};
struct age{};
struct name{};
// Define a multi_index_container of employees with following indices:
//   - a unique index sorted by employee::int,
//   - a non-unique index sorted by employee::name,
//   - a non-unique index sorted by employee::age.
typedef bmi::multi_index_container<
  employee,
  bmi::indexed_by<
    bmi::ordered_unique
      <bmi::tag<id>,  BOOST_MULTI_INDEX_MEMBER(employee,int,id)>,
    bmi::ordered_non_unique<
      bmi::tag<name>,BOOST_MULTI_INDEX_MEMBER(employee,shm_string,name)>,
    bmi::ordered_non_unique
      <bmi::tag<age>, BOOST_MULTI_INDEX_MEMBER(employee,int,age)> >,
  managed_shared_memory::allocator<employee>::type
> employee_set;
int main ()
{
   //Remove shared memory on construction and destruction
   struct shm_remove
   {
      shm_remove() { shared_memory_object::remove("MySharedMemory"); }
      ~shm_remove(){ shared_memory_object::remove("MySharedMemory"); }
   } remover;
   //Create shared memory
   managed_shared_memory segment(create_only,"MySharedMemory", 65536);
   //Construct the multi_index in shared memory
   employee_set *es = segment.construct<employee_set>
      ("My MultiIndex Container")            //Container's name in shared memory
      ( employee_set::ctor_args_list()
      , segment.get_allocator<employee>());  //Ctor parameters
   //Now insert elements
   char_allocator ca(segment.get_allocator<char>());
   es->insert(employee(0,31, "Joe", ca));
   es->insert(employee(1,27, "Robert", ca));
   es->insert(employee(2,40, "John", ca));
   return 0;
}

Программисты могут размещатьBoost.CircularBufferконтейнеры в памяти sharecd при условии, что они отключают средства отладки с определениями<BOOST_CB_DISABLE_DEBUG>или более общими<NDEBUG>. Причина в том, что эти средства отладки совместимы только с необработанными указателями.


PrevUpHomeNext

Статья Allocators, containers and memory allocation algorithms раздела The Boost C++ Libraries BoostBook Documentation Subset Chapter 16. Boost.Interprocess может быть полезна для разработчиков на c++ и boost.




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.



:: Главная :: Chapter 16. Boost.Interprocess ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-30 11:47:00
2025-05-19 12:51:51/0.018303871154785/0