工作中用到了tbb::concurrent_hash_map,但是同时用到了boost的serialize,boost没有提供对tbb::concurrent_hash_map的序列化支持,因此自己参考boost对stl中hash_map的实现,实现了使用boost库序列化tbb::concurrent_hash_map。本人水平有限,boost中hash_map序列化相关代码未完全看透,下面代码不保证在各种环境的可靠性。如果文中有错误,欢迎不吝指点。
实现文件:concurrent_hash_map.hpp
#ifndef _CONCURRENT_HASH_MAP_HPP_ #define _CONCURRENT_HASH_MAP_HPP_ #include "tbb/concurrent_hash_map.h" #include <boost/serialization/utility.hpp> #include <boost/serialization/unordered_collections_save_imp.hpp> #include <boost/serialization/unordered_collections_load_imp.hpp> #include <boost/serialization/split_free.hpp> namespace boost { namespace serialization { namespace tbb { template<class Archive, class Container> struct archive_input_hash_map { inline void operator()( Archive &ar, Container &s, const unsigned int v ){ typedef typename Container::value_type type; detail::stack_construct<Archive, type> t(ar, v); // borland fails silently w/o full namespace ar >> boost::serialization::make_nvp("item", t.reference()); //-▽-modified by me 2016/1/20---------// Container::accessor a; bool bResult = s.insert(a, t.reference()); //std::pair<typename Container::const_iterator, bool> result = // s.insert(t.reference()); // note: the following presumes that the map::value_type was NOT tracked // in the archive. This is the usual case, but here there is no way // to determine that. //if (result.second){ // ar.reset_object_address( // &(result.first->second), // &t.reference().second // ); //} if (bResult) { ar.reset_object_address( &(a->second), &t.reference().second ); } //-△-modified by me 2016/1/20----------// } }; template<class Archive, class Container> inline void save_hash_collection(Archive & ar, const Container &s) { collection_size_type count(s.size()); const collection_size_type bucket_count(s.bucket_count()); const item_version_type item_version( version<typename Container::value_type>::value ); #if 0 /* should only be necessary to create archives of previous versions * which is not currently supported. So for now comment this out */ boost::archive::library_version_type library_version( ar.get_library_version() ); // retrieve number of elements if (boost::archive::library_version_type(6) != library_version){ ar << BOOST_SERIALIZATION_NVP(count); ar << BOOST_SERIALIZATION_NVP(bucket_count); } else{ // note: fixup for error in version 6. collection size was // changed to size_t BUT for hashed collections it was implemented // as an unsigned int. This should be a problem only on win64 machines // but I'll leave it for everyone just in case. const unsigned int c = count; const unsigned int bc = bucket_count; ar << BOOST_SERIALIZATION_NVP(c); ar << BOOST_SERIALIZATION_NVP(bc); } if (boost::archive::library_version_type(3) < library_version){ // record number of elements // make sure the target type is registered so we can retrieve // the version when we load ar << BOOST_SERIALIZATION_NVP(item_version); } #else ar << BOOST_SERIALIZATION_NVP(count); ar << BOOST_SERIALIZATION_NVP(bucket_count); ar << BOOST_SERIALIZATION_NVP(item_version); #endif typename Container::const_iterator it = s.begin(); while (count-- > 0){ // note borland emits a no-op without the explicit namespace boost::serialization::save_construct_data_adl( ar, &(*it), boost::serialization::version< typename Container::value_type >::value ); ar << boost::serialization::make_nvp("item", *it++); } } template<class Archive, class Container, class InputFunction> inline void load_hash_collection(Archive & ar, Container &s) { collection_size_type count; collection_size_type bucket_count; boost::serialization::item_version_type item_version(0); boost::archive::library_version_type library_version( ar.get_library_version() ); // retrieve number of elements if (boost::archive::library_version_type(6) != library_version){ ar >> BOOST_SERIALIZATION_NVP(count); ar >> BOOST_SERIALIZATION_NVP(bucket_count); } else{ // note: fixup for error in version 6. collection size was // changed to size_t BUT for hashed collections it was implemented // as an unsigned int. This should be a problem only on win64 machines // but I'll leave it for everyone just in case. unsigned int c; unsigned int bc; ar >> BOOST_SERIALIZATION_NVP(c); count = c; ar >> BOOST_SERIALIZATION_NVP(bc); bucket_count = bc; } if (boost::archive::library_version_type(3) < library_version){ ar >> BOOST_SERIALIZATION_NVP(item_version); } s.clear(); #if ! defined(__MWERKS__) //s.resize(bucket_count); //delete by me 2016/1/20 #endif InputFunction ifunc; while (count-- > 0){ ifunc(ar, s, item_version); } } } } } template< class Archive, class Key, class HashFcn, class EqualKey, class Allocator > inline void serialize( Archive & ar , tbb::concurrent_hash_map<Key, HashFcn, EqualKey, Allocator> &t, const unsigned int file_version) { boost::serialization::split_free(ar, t, file_version); } template< class Archive, class Key, class HashFcn, class EqualKey, class Allocator > inline void save( Archive & ar, const tbb::concurrent_hash_map < Key, HashFcn, EqualKey, Allocator > &t, const unsigned int file_version ){ boost::serialization::tbb::save_hash_collection < Archive, tbb::concurrent_hash_map< Key, HashFcn, EqualKey, Allocator > > (ar, t); } template< class Archive, class Key, class HashFcn, class EqualKey, class Allocator > inline void load( Archive & ar, tbb::concurrent_hash_map< Key, HashFcn, EqualKey, Allocator > &t, const unsigned int file_version ){ boost::serialization::tbb::load_hash_collection < Archive, tbb::concurrent_hash_map< Key, HashFcn, EqualKey, Allocator >, boost::serialization::tbb::archive_input_hash_map < Archive, tbb::concurrent_hash_map< Key, HashFcn, EqualKey, Allocator > > > (ar, t); } #endif _CONCURRENT_HASH_MAP_HPP_
#include "stdafx.h" #include <stdio.h> #include <iostream> #include <fstream> #include <iosfwd> #include "boost/archive/binary_iarchive.hpp" #include "boost/archive/binary_oarchive.hpp" #include "tbb/concurrent_hash_map.h" #include "concurrent_hash_map.hpp" struct tbb_hash_func //hash 函数 { // 取hash value static size_t hash( std::string str) { std::hash<std::string> hash_fn; return hash_fn(std::string(str)); } static bool equal(const std::string str1, const std::string str2) { return memcmp(&str1, &str2, sizeof(str2)) == 0 ? true : false; } }; typedef tbb::concurrent_hash_map<std::string, int, tbb_hash_func> Int_TableType; typedef tbb::concurrent_hash_map < std::string, std::string, tbb_hash_func >String_TableType; void write_test() { std::fstream file("Text.big", std::ios::binary | std::ios::out); Int_TableType Table; Int_TableType::accessor a; Table.insert(a, Int_TableType::value_type(std::string("First"), 1)); Table.insert(a, Int_TableType::value_type(std::string("Second"), 2)); Table.insert(a, Int_TableType::value_type(std::string("Third"), 3)); Table.insert(a, Int_TableType::value_type(std::string("Fourth"), 4)); Table.insert(a, Int_TableType::value_type(std::string("Fifth"), 5)); Table.insert(a, Int_TableType::value_type(std::string("Sixth"), 6)); Table.insert(a, Int_TableType::value_type(std::string("Seventh"), 7)); for (int i = 0; i < 1000; ++i) { if ( i<200) Table.insert(a, Int_TableType::value_type(std::string("X"), 8 + i)); else if ( i>=200 && i<400 ) Table.insert(a, Int_TableType::value_type(std::string("S"), 8 + i)); else if ( i>=400 && i<600 ) Table.insert(a, Int_TableType::value_type(std::string("Y"), 8 + i)); else if ( i>=600 && i<800 ) Table.insert(a, Int_TableType::value_type(std::string("Z"), 8 + i)); else if (i>=800 ) Table.insert(a, Int_TableType::value_type(std::string("W"), 8 + i)); } Int_TableType::iterator it = Table.begin(); for (; it != Table.end(); ++it) { std::cout << it->first << "---" << it->second << std::endl; } std::cout << "SIZE:"<< Table.size() << std::endl; boost::archive::binary_oarchive os(file); os << Table; file.close(); } void read_test() { std::fstream file("Text.big", std::ios::binary | std::ios::in); Int_TableType Table; boost::archive::binary_iarchive is(file); is >> Table; std::cout << "-------------------------------------------" << std::endl; Int_TableType::iterator it = Table.begin(); for (; it != Table.end(); ++it) { std::cout << it->first << "---" << it->second << std::endl; } std::cout << "SIZE:" << Table.size() << std::endl; } int _tmain(int argc, _TCHAR* argv[]) { write_test(); read_test(); system("pause"); return 0; }