Boost C++ 库

...世界上最受尊敬和专业设计的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu, C++ 编码标准

PrevUpHomeNext

分配器和内存分配算法

进程间分配器简介
隔离存储节点分配器
自适应池节点分配器

如前所述,Boost.Interprocess 提供使用托管内存段(托管共享内存、托管映射文件...)的原始内存分配和对象构造,用户的首要请求之一是在托管共享内存中使用容器。为了实现这一点,Boost.Interprocess 利用托管内存段的内存分配算法来构建几种内存分配方案,包括通用分配器和节点分配器。

Boost.Interprocess 分配器可通过模板参数配置。分配器根据作为模板参数传递的段管理器的 void_pointer typedef 定义其 pointer typedef。当此 segment_manager::void_pointer 是相对指针时(例如,offset_ptr<void>),用户可以将这些分配器放置在多个进程中不同基地址的内存映射中。

容器分配器通常是可默认构造的,因为它们是无状态的。std::allocatorBoost.Pool'sboost::pool_allocator/boost::fast_pool_allocator 是可默认构造分配器的示例。

另一方面,Boost.Interprocess 分配器需要从具体的内存段而不是从系统范围的内存源(如堆)分配内存。Boost.Interprocess 分配器是 有状态的,这意味着必须配置它们以告知它们共享内存或内存映射文件的位置。

此信息在编译时和运行时传输:分配器接收定义段管理器类型的模板参数,并且它们的构造函数接收指向用户想要分配值的托管内存段的段管理器的指针。

Boost.Interprocess 分配器 没有默认构造函数,容器必须使用配置的分配器显式初始化

//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.Interprocess 分配器还具有 get_segment_manager() 函数,该函数返回它们在构造函数中接收的底层段管理器

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

当交换 STL 容器时,关于如何处理分配器存在积极的讨论。一些 STL 实现,例如 Visual .NET 2003 中的 Dinkumware,当分配器不相等时,通过临时变量执行整个容器的深度交换。提议的解决方案 是分配器应该以非抛出的方式交换。

不幸的是,这种方法对于共享内存无效。使用堆分配器,如果节点分配器的 Group1 共享一个公共隔离存储,而 Group2 共享另一个公共隔离存储,则只需简单的指针交换即可交换 Group1 的分配器和 Group2 的另一个分配器。但是,当用户想要交换两个共享内存分配器时,每个分配器都放置在不同的共享内存段中,这是不可能的。由于通常共享内存在每个进程中映射到不同的地址,因此放置在一个段中的指针无法指向放置在另一个共享内存段中的任何对象,因为在每个进程中,段之间的距离是不同的。但是,如果两个共享内存分配器都在同一段中,则可以进行非抛出交换,就像堆分配器一样。

在最终解决方案达成之前。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 {

分配器仅提供所需的 typedef,并将所有分配和释放请求转发给构造函数中传递的段管理器,就像 std::allocator 将请求转发给 operator new[] 一样。

使用 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("MyName"); }
      ~shm_remove(){ shared_memory_object::remove("MyName"); }
   } remover;

   //Create shared memory
   managed_shared_memory segment(create_only,"MyName", 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 提供 3 个基于此隔离存储算法的分配器:node_allocator, private_node_allocatorcached_node_allocator

要了解隔离存储池实现的详细信息,请参阅 Boost.Interprocess 隔离存储池的实现 部分。

node_allocator, private_node_allocatorcached_node_allocator 实现了标准分配器接口和 Boost.Interprocess 分配器的属性 中解释的函数。

所有这些分配器都由 3 个参数模板化

  • class T: 要分配的类型。
  • class SegmentManager: 将在构造函数中传递的段管理器的类型。
  • std::size_t NodesPerChunk: 一个内存块将包含的节点数。此值将定义当池用完节点时池将向段管理器请求的内存大小。此参数具有默认值。

这些分配器还提供 deallocate_free_chunks() 函数。此函数将遍历池的所有内存块,并将空闲内存块返回给托管内存段。如果未使用此函数,则在池被销毁之前不会释放空闲块,因此在销毁池之前将池分配的内存返回给段的唯一方法是手动调用此函数。此函数非常耗时,因为它具有二次复杂度 (O(N^2))。

对于堆内存节点分配器(如 Boost.Pool'sboost::fast_pool_allocator),通常为每个节点大小使用全局的线程共享单例池。如果您尝试在进程之间共享节点分配器,则这是不可能的。为了实现这种共享,node_allocator 使用段管理器唯一的类型分配服务(请参阅 唯一实例构造 部分)。

在初始化时,node_allocator 对象在段中搜索此唯一对象。如果它未预设,则构建一个。这样,在内存段内构建的所有 node_allocator 对象共享一个唯一的内存池。

公共隔离存储不仅在相同类型的 node_allocator 之间共享,而且还在分配相同大小对象的所有节点分配器之间共享,例如,node_allocator<uint32>node_allocator<float32>。这节省了大量内存,但也为每个节点分配带来了同步开销。

动态创建的公共隔离存储集成了引用计数,以便 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("MyName"); }
      ~shm_remove(){ shared_memory_object::remove("MyName"); }
   } remover;

   //Create shared memory
   managed_shared_memory segment(create_only,
                                 "MyName",  //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 在分配相同大小对象的节点分配器之间共享公共隔离存储,这优化了内存使用。但是,它需要唯一的/命名的对象构造功能,以便实现这种共享。由于这种共享,还为每个节点分配带来了同步开销。有时,唯一对象服务不可用(例如,在构建索引类型以实现命名分配服务本身时)或同步开销是不可接受的。很多时候,程序员希望确保在分配器被销毁时池也被销毁,以便尽快释放内存。

因此,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("MyName"); }
      ~shm_remove(){ shared_memory_object::remove("MyName"); }
   }  remover;

   //Create shared memory
   managed_shared_memory segment(create_only,
                                 "MyName",  //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,提供用于控制缓存的附加功能(缓存可以按实例控制)

  • void set_max_cached_nodes(std::size_t n): 设置最大缓存节点限制。如果缓存节点达到限制,则将一些节点返回到共享池。
  • std::size_t get_max_cached_nodes() const: 返回最大缓存节点限制。
  • void deallocate_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("MyName"); }
      ~shm_remove(){ shared_memory_object::remove("MyName"); }
   } remover;

   //Create shared memory
   managed_shared_memory segment(create_only, "MyName", 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.Interprocess 提供 3 个新的分配器:adaptive_pool, private_adaptive_pool, cached_adaptive_pool

adaptive_pool, private_adaptive_poolcached_adaptive_pool 实现了标准分配器接口和 Boost.Interprocess 分配器的属性 中解释的函数。

所有这些分配器都由 4 个参数模板化

  • class T: 要分配的类型。
  • class SegmentManager: 将在构造函数中传递的段管理器的类型。
  • std::size_t NodesPerChunk: 一个内存块将包含的节点数。此值将定义当池用完节点时池将向段管理器请求的内存大小。此参数具有默认值。
  • std::size_t MaxFreeChunks: 池将保留的最大空闲块数。如果达到此限制,池会将块返回到段管理器。此参数具有默认值。

这些分配器还提供 deallocate_free_chunks() 函数。此函数将遍历池的所有内存块,并将空闲内存块返回给托管内存段。此函数比隔离存储分配器快得多,因为自适应池算法提供对空闲块的恒定时间访问。

就像 node_allocator 一样,为每个节点大小使用全局的进程线程池。在初始化时,adaptive_pool 在段中搜索池。如果它未预设,则构建一个。自适应池是使用唯一名称创建的。自适应池也在分配相同大小对象的所有节点分配器之间共享,例如,adaptive_pool<uint32>adaptive_pool<float32>

当附加到池的所有分配器都被销毁时,公共自适应池也被销毁。

相等性: 使用相同段管理器构造的两个 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("MyName"); }
      ~shm_remove(){ shared_memory_object::remove("MyName"); }
   } remover;

   //Create shared memory
   managed_shared_memory segment(create_only,"MyName", 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("MyName"); }
      ~shm_remove(){ shared_memory_object::remove("MyName"); }
   } remover;

   //Create shared memory
   managed_shared_memory segment(create_only,
                                 "MyName",  //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_poolsadaptive_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 提供了额外的函数来控制缓存(缓存可以针对每个实例进行控制)

  • void set_max_cached_nodes(std::size_t n): 设置最大缓存节点限制。如果缓存节点达到限制,则将一些节点返回到共享池。
  • std::size_t get_max_cached_nodes() const: 返回最大缓存节点限制。
  • void deallocate_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("MyName"); }
      ~shm_remove(){ shared_memory_object::remove("MyName"); }
   } remover;

   //Create shared memory
   managed_shared_memory segment(create_only,"MyName", 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;
}

PrevUpHomeNext