提问人:Moritz Perschke 提问时间:11/11/2023 最后编辑:Moritz Perschke 更新时间:11/13/2023 访问量:46
在对象的构造函数中打开 Boost 进程间段
Opening Boost Interprocess segment in Constructor of object
问:
我在一个进程的 Boost Interprocess 中创建了一个,然后想在另一个进程中打开它以不断写入它。
另一个进程尝试在对象的构造函数中打开共享内存,然后提供一个方法。map<int, vector<int>>
managed_shared_memory
Communication
addData(int _data)
例:
// Typedefs for shared data structures
using boost::interprocess;
using std::pair;
using std::scoped_allocator_adaptor;
typedef managed_shared_memory::segment_manager man;
typedef allocator <int , man> Int_Allocator;
typedef vector <int, Int_Allocator> Int_Vector;
typedef pair <const int, Int_Vector> Int_Vector_Map_Type;
typedef scoped_allocator_adaptor <allocator<Int_Vector_Map_Type, man>> Int_Vector_Map_Type_Allocator;
typedef map <int, Int_Vector, std::less<int>, Int_Vector_Map_Type_Allocator> Int_Vector_Map;
// Let's call this process 'parent'
managed_shared_memory segment(create_only, "Shared");
Int_Vector_Map_Type_Allocator alloc = segment.get_segment_manager();
segment.construct<Int_Vector_Map>("Map")(std::less<int>(), alloc);
// 'child' process
class Communciation{
Int_Vector_Map* map;
public:
Communication{
managed_shared_memory segment(open_only, "Shared");
map = segment.find<Int_Vector_Map>("Map").first;
}
void addData(int _key, int _value){
if(map->size() == 0 || map->find(_key) == map->end(){
managed_shared_memory segment(open_only, "Shared");
Int_Allocator alloc = segment.get_segment_manager();
map->insert(Int_Vector_Map_Type(_key, Int_Vector(alloc)));
} // create the vector and its allocator, otherwise runtime error
map->at(_key).push_back(_value);
}
}
这是我正在查看的内容的细分示例,但我没有看到直接的错误。
尝试运行此代码时,出现运行时错误“未处理的异常:EXCEPTION_ACCESS_VIOLATION读取地址 (...)”。
将方法更改为:addData(...)
void addData(int _key, int _value){
managed_shared_memory segment(open_only, "Shared");
map = segment.find<Int_Vector_Map>("Map").first;
if(map->size() == 0 || map->find(_key) == map->end(){
Int_Allocator alloc = segment.get_segment_manager();
map->insert(Int_Vector_Map_Type(_key, Int_Vector(alloc)));
} // create the vector and its allocator, otherwise runtime error
map->at(_key).push_back(_value);
}
即每次调用都打开地图可以解决此问题,但不适用于我的情况,因为我希望能够在每帧多次调用此方法而不会对 fps 产生太大影响。
导致此问题的原因是什么,是否可以按照所述使用boost的交互进程?
编辑:为了增加上下文,“子”进程正在另一个进程中运行,我在其中注入了 dll。
答:
1赞
sehe
11/12/2023
#1
答案有很多组成部分:
- 使用简化的类型别名
- 该区段必须保持映射状态,因此您必须将其作为类中的成员(或具有更长的生存期)
- 只有当一致地使用作用域分配器并且插入使用分配器构造(例如 方法)
emplace
我在使用 Boost Container 的实现方面有很好的经验,所以让我们展示一下。
简化的类型别名
// Typedefs for shared data structures
namespace bip = boost::interprocess;
namespace Shared {
namespace bc = boost::container;
using Segment = bip::managed_shared_memory;
using Mgr = Segment::segment_manager;
template <typename T> using Alloc = bc::scoped_allocator_adaptor<bip::allocator<T, Mgr>>;
template <typename T> using Vector = bc::vector<T, Alloc<T>>;
template <typename K, typename V, typename Cmp = std::less<K>, typename P = std::pair<K const, V>>
using Map = bc::map<K, V, Cmp, Alloc<P>>;
} // namespace Shared
现在您可以简单地声明:
using IVMap = Shared::Map<int, Shared::Vector<int>>; // Int_Vector_Map
它将扩展到正确的比较器、分配器等。
辈子
若要从构造函数构造段,需要使用初始值设定项列表。例如:
class Communication {
public:
Communication() //
: segment_{bip::open_only, "Shared"}
, map_(*segment_.find<IVMap>("Map").first) {}
void addData(int key, int value);
private:
Shared::Segment segment_;
IVMap& map_;
};
Emplace(安居酒店)
要插入新元素,请实现如下:addData
void addData(int key, int value) {
auto it = map_.find(key);
if (it == map_.end())
it = map_.emplace(key).first;
it->second.push_back(value);
}
请注意,该区段仅存在。请注意,分配器是由于作用域分配器适配器与结构相结合而传播的。emplace
编辑可悲的是,我无法使 emplace 编译。我敢肯定我错过了一些微不足道的东西,但我已经解决了它,比如:
if (it == map_.end()) {
IVMap::mapped_type v(map_.get_allocator());
it = map_.emplace(key, std::move(v)).first;
}
现场演示
#include <boost/container/scoped_allocator.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/containers/map.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/managed_mapped_file.hpp>
#include <boost/interprocess/managed_shared_memory.hpp>
#include <set>
// Typedefs for shared data structures
namespace bip = boost::interprocess;
namespace Shared {
namespace bc = boost::container;
#ifdef COLIRU // online compiler
using Segment = bip::managed_mapped_file;
#else
using Segment = bip::managed_shared_memory;
#endif
using Mgr = Segment::segment_manager;
template <typename T> using Alloc = bc::scoped_allocator_adaptor<bip::allocator<T, Mgr>>;
template <typename T> using Vector = bc::vector<T, Alloc<T>>;
template <typename K, typename V, typename Cmp = std::less<K>, typename P = std::pair<K const, V>>
using Map = bc::map<K, V, Cmp, Alloc<P>>;
} // namespace Shared
using IVMap = Shared::Map<int, Shared::Vector<int>>; // Int_Vector_Map
class Communication {
public:
Communication() //
: segment_{bip::open_only, "Shared"}
, map_(*segment_.find<IVMap>("Map").first) {}
void addData(int key, int value) {
auto it = map_.find(key);
if (it == map_.end()) {
IVMap::mapped_type v(map_.get_allocator());
it = map_.emplace(key, std::move(v)).first;
}
it->second.push_back(value);
}
auto const& get() const { return map_; }
private:
Shared::Segment segment_;
IVMap& map_;
};
#include <fmt/ranges.h>
#include <functional>
#include <random>
static auto vals = bind(std::uniform_int_distribution(100, 999), std::mt19937{std::random_device{}()});
int main(int argc, char** argv) {
auto const args = std::set<std::string_view>(argv + 1, argv + argc);
if (args.contains("parent")) {
Shared::Segment seg{bip::open_or_create, "Shared", 10 << 10};
seg.find_or_construct<IVMap>("Map")(seg.get_segment_manager());
}
if (args.contains("child")) {
Communication comm;
for (unsigned n = 10; n--;) {
auto v = vals();
comm.addData(v / 100, v);
}
fmt::print("After insertion:\n - {}\n", fmt::join(comm.get(), "\n - "));
}
}
印刷
+ ./a.out parent
+ ./a.out child
After insertion:
- (1, [155, 170])
- (2, [248])
- (4, [418])
- (5, [542, 562])
- (6, [642, 674, 659])
- (7, [783])
+ ./a.out child
After insertion:
- (1, [155, 170, 143, 130])
- (2, [248, 222])
- (3, [325])
- (4, [418, 428])
- (5, [542, 562, 556])
- (6, [642, 674, 659, 671])
- (7, [783, 793, 733, 745])
+ ./a.out child
After insertion:
- (1, [155, 170, 143, 130])
- (2, [248, 222])
- (3, [325, 320, 362])
- (4, [418, 428, 486, 437])
- (5, [542, 562, 556])
- (6, [642, 674, 659, 671, 695, 609])
- (7, [783, 793, 733, 745, 786, 777, 793])
- (9, [995])
评论
0赞
sehe
11/12/2023
使用仔细的组合,我能够稍微接近无痛插入的目标: coliru.stacked-crooked.com/a/70e948f8285d180d 不过,这是我第一次看到与(作用域)分配器相关的任何内容在 libstdc++ 实现中比在 boost 实现中效果更好。我仍然会关注这里的提升类型。std::scoped_allocator_adaptor
boost::container::map
0赞
Moritz Perschke
11/13/2023
感谢您对 DS 的出色实施。但问题仍然存在,即在共享内存中指向 DS 的指针会导致运行时错误“未处理的异常:EXCEPTION_ACCESS_VIOLATION读取地址 (...)”这在理论上应该有效,而我的问题在别处吗?最让我困惑的是读取地址错误
0赞
sehe
11/13/2023
不过,事实并非如此?我提供了一个工作示例,您可以看到它为自己工作。你试过了吗?也许你可以基于一个小的、独立的例子来说明你的问题。(换句话说,您的问题可能出在“其他地方”,因为“其他地方”可能在答案代码中得到了确切的解决,即项目符号 #2(“该段必须保持映射状态,因此您必须将其作为类中的成员(或具有更长的生存期)”)。不过这只是我最好的猜测,因为我看不出你是如何调整代码的)
0赞
Moritz Perschke
11/13/2023
是的,我只是认为问题出在我的 DS 实现上,因为我对进程间的深度有点不了解,谢谢你的澄清,问题必须在其他地方。
0赞
sehe
11/14/2023
不清楚你的意思。如果你的意思是,是的,你可以,但你必须相应地调整其他代码(因为不能从 转换)。谨慎这些似乎都没有用?它不会在共享内存中工作,因为动态分配将在共享内存段之外(即 一如既往地从堆中分配)。专业提示:您始终可以只显示神秘的编译器消息: coliru.stacked-crooked.com/a/3093530e54d94039std::vector
std::allocator<>
bip::allocator<>
std::vector
评论
vector<string>
multimap
unordered_multimap
map<int, vector>
scoped_allocator_adaptor
adaptive_pool
allocator