在共享内存中使用boost :: interprocess allocator的std :: unordered_map - 缺点?

Ell*_*ark 3 c++ containers boost stl interprocess

我现在正在使用共享内存boost::interprocess.

我已经按以下方式定义了一些std::unordered_mapstd::unordered_set类型:

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

#include <unordered_map> // NOT the boost implementation ...

...

namespace ipc = boost::interprocess;

 /**
 * allocator type needed to construct maps in shared memory
 */
typedef ipc::allocator<std::pair<const size_t, std::string>,
           ipc::managed_shared_memory::segment_manager> OBJ_MAP_ALLOCATOR;

/**
 * map type to construct maps in shared memory
 */
typedef std::unordered_map<size_t,
               std::string,
               std::hash<size_t>,
               std::equal_to<size_t>,
               OBJ_MAP_ALLOCATOR> OBJ_MAP_TYPE;
Run Code Online (Sandbox Code Playgroud)

我把它们初始化为:

ipc::managed_shared_memory segment;

// allocate segment etc ... 

OBJ_MAP_ALLOCATOR alloc_inst(segment.get_segment_manager());
objMap = segment.find_or_construct<OBJ_MAP_TYPE> (ipc::unique_instance)(alloc_inst);
Run Code Online (Sandbox Code Playgroud)

这似乎工作正常,我没有在编译或运行时(在Apple LLVM version 9.1.0 (clang-902.0.39.1)使用C++ 14标准的macOS上工作)发现任何问题.

在Boost文档中,仅提到了Boost容器或特定于进程间的实现.不幸的是,它们似乎不包含无序版本.

那么,我想知道使用默认STL容器与Boost分配器有什么问题吗?也许在不同的平台上?

任何暗示赞赏!

更新:

我想知道它是否在不同的环境中工作,所以我写了一个关于Coliru的最小例子(令人惊讶地工作std::string):

http://coliru.stacked-crooked.com/a/91d1a143778cf3e9

seh*_*ehe 5

unordered_map 将处理Boost进程间分配器IFF你的库实现支持使用非原始指针类型的有状态分配器¹和分配​​器.

即便如此,就像@rustyx提到的那样,如果你真的与另一个进程共享内存,那么你将陷入深深的麻烦.另一个进程可能会将段映射到不同的基址,使存储在内存区域内的所有指针都无效.

☞您还需要使用带有字符串的Interprocess分配器!

这是我通常喜欢写的内容:

#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>

#include <unordered_map>

namespace ipc = boost::interprocess;

namespace Shared {
    using Segment = ipc::managed_shared_memory;
    using Manager = Segment::segment_manager;
    template <typename T> using Alloc = ipc::allocator<T, Manager>;
    template <typename K, typename V, typename KH = std::hash<K>, typename KEq = std::equal_to<K> >
        using HashMap = std::unordered_map<K, V, KH, KEq, Alloc<std::pair<const K, V>> >;

    using String = ipc::basic_string<char, std::char_traits<char>, Alloc<char> >;
}

using OBJ_MAP_TYPE = Shared::HashMap<size_t, Shared::String>;

int main() {
    Shared::Segment msm(ipc::open_or_create, "test", 10ul<<20);

    Shared::Manager* mgr = msm.get_segment_manager();
    OBJ_MAP_TYPE& m = *msm.find_or_construct<OBJ_MAP_TYPE>("aname")(msm.get_segment_manager());

    m.emplace(42, Shared::String("LtUaE", msm.get_segment_manager()));
}
Run Code Online (Sandbox Code Playgroud)

值得注意的细节:

  1. 这一点:

    Shared::Manager* mgr = msm.get_segment_manager();
    OBJ_MAP_TYPE& m = *msm.find_or_construct<OBJ_MAP_TYPE>("aname")(mgr);
    
    Run Code Online (Sandbox Code Playgroud)

    这是一个方便的捷径:

    Shared::Alloc<OBJ_MAP_TYPE::value_type> alloc_inst(msm.get_segment_manager());
    OBJ_MAP_TYPE& m = *msm.find_or_construct<OBJ_MAP_TYPE>("aname")(alloc_inst);
    
    Run Code Online (Sandbox Code Playgroud)

    这是有效的,因为允许从段管理器指针到分配器实例的隐式转换.

输入MAGIC

您会注意到嵌套分配器使用起来很笨拙:

m.emplace(42, Shared::String("LtUaE", msm.get_segment_manager()));
Run Code Online (Sandbox Code Playgroud)

这就是设计师scoped_allocator_adaptor试图解决的问题.如果将分配器更改为:

template <typename T> using Alloc = std::scoped_allocator_adaptor<ipc::allocator<T, Manager> >;
Run Code Online (Sandbox Code Playgroud)

你可以突然写下:

m.emplace(42, "LtUaE");
Run Code Online (Sandbox Code Playgroud)

这是因为就地构造是根据uses-allocator构造定义的(参见[allocator.uses.construction])

看见 Live On Coliru


¹准备惊讶,@ SergeyA.Libstdc ++上次我检查时不支持这个,但是unordered_map自GCC 4.9.0以来它支持它,并且OP似乎有证据证明libc ++有(虽然我们甚至不知道是否有一个typedef的实例:) )