STL可以说是C++的重要的组成部分,它提供了一系列方便使用的容器,可以供我们存放数据,节省了很多开发时间。不过如果打算在多个进程之间共享一个STL容器,却常常会遭遇一些困难。比如你正在使用一个map,一般的IPC方法,可能下意识的你就会选择使用共享内存,如果可以把这个map放入一块共享内存供多个进程操作,那该多方便呢。然而问题却没这么简单,由于STL帮我们完美的封装好了map的内部方法,包括内存分配方案,这也导致了我们没法把map直接简单的用placement new放置到已知的共享内存上。
要完成这个任务就需要我们自己实现一个基于共享内存的allocator,替换map默认的allocator,在这个allocator中实现map的内存分配方案。关于allocator的讲解可以看侯捷的《STL源码剖析》这本书,写一个简单的allocator直接套下边的模板就可以了,主要实现的是这几个函数:
-
max_size()容器的最大容量 -
allocate(num)为num个元素分配内存 -
construct(p)将p所指的元素初始化 -
destroy(p)销毁p所指向的元素 -
deallocate(p, num)收回p所指的num个空间
- #include <limits>
- #include <iostream>
- namespace MyLib {
- template <class T>
- class MyAlloc {
- public:
- // type definitions
- typedef T value_type;
- typedef T* pointer;
- typedef const T* const_pointer;
- typedef T& reference;
- typedef const T& const_reference;
- typedef std::size_t size_type;
- typedef std::ptrdiff_t difference_type;
- // rebind allocator to type U
- template <class U>
- struct rebind {
- typedef MyAlloc<U> other;
- };
- // return address of values
- pointer address (reference value) const {
- return &value;
- }
- const_pointer address (const_reference value) const {
- return &value;
- }
- /* constructors and destructor
- * - nothing to do because the allocator has no state
- */
- MyAlloc() throw() {
- }
- MyAlloc(const MyAlloc&) throw() {
- }
- template <class U>
- MyAlloc (const MyAlloc<U>&) throw() {
- }
- ~MyAlloc() throw() {
- }
- // return maximum number of elements that can be allocated
- size_type max_size () const throw() {
- return std::numeric_limits<std::size_t>::max() / sizeof(T);
- }
- // allocate but don't initialize num elements of type T
- pointer allocate (size_type num, const void* = 0) {
- // print message and allocate memory with global new
- std::cerr << "allocate " << num << " element(s)"
- << " of size " << sizeof(T) << std::endl;
- pointer ret = (pointer)(::operator new(num*sizeof(T)));
- std::cerr << " allocated at: " << (void*)ret << std::endl;
- return ret;
- }
- // initialize elements of allocated storage p with value value
- void construct (pointer p, const T& value) {
- // initialize memory with placement new
- new((void*)p)T(value);
- }
- // destroy elements of initialized storage p
- void destroy (pointer p) {
- // destroy objects by calling their destructor
- p->~T();
- }
- // deallocate storage p of deleted elements
- void deallocate (pointer p, size_type num) {
- // print message and deallocate memory with global delete
- std::cerr << "deallocate " << num << " element(s)"
- << " of size " << sizeof(T)
- << " at: " << (void*)p << std::endl;
- ::operator delete((void*)p);
- }
- };
- // return that all specializations of this allocator are interchangeable
- template <class T1, class T2>
- bool operator== (const MyAlloc<T1>&,
- const MyAlloc<T2>&) throw() {
- return true;
- }
- template <class T1, class T2>
- bool operator!= (const MyAlloc<T1>&,
- const MyAlloc<T2>&) throw() {
- return false;
- }
- }
如果要使用这个allocator的话,可以这样:
- #include <vector>
- #include "myalloc.hh"
- int main()
- {
- // create a vector, using MyAlloc<> as allocator
- std::vector<int,MyLib::MyAlloc<int> > v;
- // insert elements
- // - causes reallocations
- v.push_back(42);
- }
不过写出这样一个简单的vector内存分配器是很容易,不过如果要针对map或者hash_map写一份高效鲁棒的内存分配器却也不是一件容易的事情。一个想法就是在共享内存上划分出内存池,自己对这块内存池进行维护,并且还得考虑进程同步问题。然而,一件幸运的事情是,在boost项目第1.35个版本之后,已经增加了一个叫做boost::interprocess的库,其中包含了关于多线程和多进程的很多有用的辅助类库,当中也有为STL容器所写的allocator,这里有一系列非常棒的示例程序。
不过其中有一个注意点,由于allocator是以类的形式提供的,所以当你在使用STL容器的时候,你需要保证allocator的实例没有被销毁,例如下边的函数希望产生mymap这个指针供其他函数使用,结果将是错误的。事实上,一旦该函数运行结束,mymap所指向的内存地址将会失效。
- typedef int KeyType;
- typedef float MappedType;
- typedef std::pair<const KeyType, MappedType> ValueType;
- typedef allocator<ValueType, managed_shared_memory::segment_manager> ShmemAllocator;
- typedef map<KeyType, MappedType, std::less<KeyType>, ShmemAllocator> MyMap;
- MyMap* get_ptr(){
- using namespace boost::interprocess;
- managed_shared_memory segment(create_only, "MySharedMemory", 65536);
- ShmemAllocator alloc_inst (segment.get_segment_manager());
- //如果你希望把mymap当作参数传递到别的函数中去处理,请保证此时alloc_inst这个实例是存在的。
- MyMap *mymap = segment.construct<MyMap>("MyMap")(std::less<KeyType>(),alloc_inst);
- return mymap;
- }
另外由 managed_shared_memory 创建的共享内存默认属性是 600。总之,结合了STL和boost库,把STL容器放入共享内存中供多进程使用,就变成了一件非常容易而且愉快的事情。
Post a Comment