在共享内存中分配的映射的映射

Maps of maps allocated in shared memory

本文关键字:映射 分配 共享 内存      更新时间:2023-10-16

在一个boost::interprocess::managed_shared_memory中,我试图在另一个boost::unordered_map中创建boost::unordered_map作为值,两个映射的键都为std::string。共享内存段内的Map in Map由两个不同的进程访问,从外部&内部贴图。

下面是我的实现&想知道这是可能的/正确的方式还是其他更好的方式?

boost::interprocess::managed_shared_memory segment(boost::interprocess::open_or_create, "BOOST_SHM", 65536);
    typedef std::string   KeyType;
    typedef std::string   ValueType;
    typedef std::pair<const KeyType, ValueType> MapType;
    typedef boost::interprocess::allocator<MapType, boost::interprocess::managed_shared_memory::segment_manager> ShmemAllocator;
    typedef boost::unordered_map<KeyType, ValueType, boost::hash<KeyType>, std::equal_to<KeyType>, ShmemAllocator> InMap;
    ShmemAllocator alloc_inst(segment.get_segment_manager());
    InMap *inside_map = segment.construct<InMap>("SHM_IN_MAP")(3, boost::hash<KeyType>(), std::equal_to<KeyType>(), alloc_inst);

    typedef std::pair<const KeyType, MapType> MIMType;
    typedef boost::interprocess::allocator<MIMType, boost::interprocess::managed_shared_memory::segment_manager> MIMShmemAllocator;
    typedef boost::unordered_map<KeyType, MapType, boost::hash<KeyType>, std::equal_to<KeyType>, MIMShmemAllocator> OutMap;
    //MIMShmemAllocator alloc_inst(segment.get_segment_manager());   /*Commented due to Error*/
    OutMap *outside_map = segment.construct<OutMap>("SHM_OUT_MAP")(3, boost::hash<KeyType>(), std::equal_to<KeyType>(), alloc_inst);

其他详细信息:

gcc版本4.8.3 20140911(Red Hat 4.8.3-9)(gcc)在CentOS7上,BOOST _LIB_VERSION"1_58"

好的。

因此,出现了一些基本错误,可能还有一些混乱。

接下来,有一些强大的技巧可以使使用带有自定义(有状态)分配器的嵌套容器更加方便。

以下是工作样本中所有三个提示的汇总,希望能有所帮助!


  1. 字符串必须也使用共享内存分配器

    否则,在另一个进程中使用这些数据将是非法的。使用字符串会导致"未定义的行为"。

    至少,让您的字符串使用共享内存分配器:

    namespace Shared {
        using Segment   = bip::managed_shared_memory;
        template <typename T>
        using Alloc     = bip::allocator<T, Segment::segment_manager>;
        using String    = boost::container::basic_string<char, std::char_traits<char>, Alloc<char> >;
        using KeyType   = String;
        using ValueType = String;
    }
    
  2. 映射分配器指定过多。在映射中封装pair<K const, v>元素的实际节点类型无论如何都是实现定义的。那么地图如何知道如何分配这些节点呢?

    它们重新绑定分配器:请参阅此处文档中的rebind

    所以,你可以通过Alloc<void>。或与Shared::String相同的分配器。地图会发现:

    typedef boost::unordered_map<KeyType, ValueType, boost::hash<KeyType>, std::equal_to<KeyType>, Alloc<void> > InMap;
    typedef boost::unordered_map<KeyType, InMap,     boost::hash<KeyType>, std::equal_to<KeyType>, Alloc<void> > OutMap;
    
  3. 现在查看电源提示

    总是通过有状态的分配器是很烦人的。它把代码搞得一团糟。幸运的是,c++11(以及c++03的Boost Containers)已经涵盖了:

    • scoped_allocator_adaptor<T...>
    • allocator_type
    • uses_allocator<T>性状

    这些帮手可以让你的生活轻松很多。在适用的情况下,它们通过将分配器向下传递给元素类型构造函数来实现这一点。自动地同样,来自反弹分配器类型的隐式转换使事情正常工作。

    因此,实际上,您只需构造一个带有正确分配器(使其成为Scoped)和一个键的外部映射,就不必一直指定分配器。

这里有一个完整的演示:

在Coliru上直播

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/container/scoped_allocator.hpp>
#include <boost/unordered_map.hpp>
#include <iostream>
namespace bip = boost::interprocess;
namespace Shared {
    using Segment = bip::managed_shared_memory;
    template <typename T>
    using Alloc   = bip::allocator<T, Segment::segment_manager>;
    using Scoped  = boost::container::scoped_allocator_adaptor<Alloc<char> >;
    using String  = boost::container::basic_string<char, std::char_traits<char>, Scoped>;
    using KeyType = String;
    typedef boost::unordered_map<KeyType, String, boost::hash<KeyType>, std::equal_to<KeyType>, Scoped> InMap;
    typedef boost::unordered_map<KeyType, InMap,  boost::hash<KeyType>, std::equal_to<KeyType>, Scoped> OutMap;
}
int main() {
    srand(time(NULL));
    Shared::Segment segment(bip::open_or_create, "BOOST_SHM", 65536);
    auto* mgr = segment.get_segment_manager();
    Shared::OutMap *p_outside_map = segment.find_or_construct<Shared::OutMap> ("SHM_OUT_MAP") (mgr);
    auto& outside_map = *p_outside_map;
    Shared::String sskey(mgr); // reduce shared allocations as they are costly (in terms of fragmentation/overhead)
    char outer_keys[3], inner_keys[3];
    std::generate_n(outer_keys, 3, [] { return rand()%26+'a'; });
    std::generate_n(inner_keys, 3, [] { return rand()%26+'a'; });
    for (auto key : outer_keys) {
        sskey = key;
        auto& inner = outside_map[sskey];
        for (auto more : inner_keys) {
            inner[sskey + "_" + more] += "value";
        }
    }
    for (auto const& oe : outside_map) {
        for (auto const& ie : oe.second) {
            std::cout << "outside_map[" << oe.first << "][" << ie.first << "] == " << ie.second << "n";
        }
    }
}

实际上,为了使它在Coliru上工作,我们需要使用一个映射文件:

在Coliru上直播

运行几次:

outside_map[s][s_t] == value
outside_map[s][s_r] == value
outside_map[s][s_c] == value
outside_map[f][f_t] == value
outside_map[f][f_r] == value
outside_map[f][f_c] == value
outside_map[o][o_t] == value
outside_map[o][o_r] == value
outside_map[o][o_c] == value

第二次运行:

outside_map[a][a_d] == value
outside_map[a][a_c] == value
outside_map[a][a_g] == value
outside_map[r][r_d] == value
outside_map[r][r_c] == value
outside_map[r][r_g] == value
outside_map[g][g_d] == value
outside_map[g][g_c] == value
outside_map[g][g_g] == value
outside_map[s][s_t] == value
outside_map[s][s_r] == value
outside_map[s][s_c] == value
outside_map[f][f_t] == value
outside_map[f][f_r] == value
outside_map[f][f_c] == value
outside_map[o][o_t] == value
outside_map[o][o_r] == value
outside_map[o][o_c] == value

请注意每次运行如何成功地将value附加到3个内部映射中的9个键。