拓冰建站拓冰建站
首页 / 资讯中心 / 正文

oneTBB concurrent_multimap 构造、析构与拷贝完全指南:构造函数家族、赋值语义与并发约束解析

oneTBB concurrent_multimap 构造、析构与拷贝完全指南构造函数家族、赋值语义与并发约束解析【免费下载链接】moldmold: A Modern Linker 项目地址: https://gitcode.com/GitHub_Trending/mo/mold本文基于 mold 仓库内置的 oneTBB 第三方源码与官方规范文档系统讲解oneapi::tbb::concurrent_multimap的构造、析构与拷贝Construction, Destruction, Copying完整语义包括 3 个空容器构造函数、4 个元素序列构造函数、拷贝/移动构造、析构函数与 3 个赋值运算符并结合concurrent_map.h的真实实现揭示其底层跳表机制、分配器传播规则与并发操作约束。读完本文你将能在多线程场景下正确、安全地创建、复制与销毁concurrent_multimap容器并理解为何部分操作在并发时属于未定义行为。一、背景concurrent_multimap 在 oneTBB 中的定位oneapi::tbb::concurrent_multimap是 oneTBB 提供的一个有序关联容器sorted associative container其核心特性在 类规范总览页 中定义它支持并发的插入insertion、查找lookup与遍历traversal但不支持并发的删除erasure与concurrent_map最大的区别是它允许存储多个键相等的元素多重映射语义。在 mold 项目中oneTBB 以第三方依赖的形式内置于third-party/tbb/目录作为高性能链接器的并行支撑库。本文讨论的concurrent_multimap生命周期语义是使用该库编写并发代码时最基础也最容易踩坑的部分——尤其是哪些操作允许并发、哪些操作并发即未定义行为这一边界。类模板的完整签名如下见 concurrent_map.htemplate typename Key, typename T, typename Compare std::lessKey, typename Allocator tbb::tbb_allocatorstd::pairconst Key, T class concurrent_multimap;默认比较器为std::lessKey默认分配器为 oneTBB 自带的tbb::tbb_allocator元素类型value_type为std::pairconst Key, T。从源码看concurrent_multimap实际继承自concurrent_skip_list并发跳表并通过map_traits模板参数AllowMultimapping true开启多重映射支持见 concurrent_map.h这正是它可以存放等键元素且并发安全的底层原因。二、空容器构造函数原规范文档construction_destruction_copying.rst定义了三组空容器构造函数concurrent_multimap(); explicit concurrent_multimap( const key_compare comp, const allocator_type alloc allocator_type() ); explicit concurrent_multimap( const allocator_type alloc );语义说明三个版本都构造一个空的concurrent_multimap。若提供比较函数对象comp则容器内所有key_type之间的比较都将使用它例如自定义排序规则、自定义结构体键的哈希等价比较逻辑。若提供分配器alloc则容器所有内存分配节点、跳表结构等都通过它完成否则使用默认构造的allocator_type()。第二个构造函数中comp与alloc可以同时提供第三个版本只提供分配器比较器使用默认值。explicit关键字意味着禁止隐式转换例如不能直接把一个key_compare对象隐式转换成concurrent_multimap必须显式写出构造函数调用这有助于避免意外的类型转换。从实现看这些构造函数在 concurrent_map.h 中通过using base_type::base_type;直接继承自基类concurrent_skip_list默认构造函数被显式声明为concurrent_multimap() default;。三、从元素序列构造迭代器区间与初始化列表3.1 迭代器区间构造template typename InputIterator concurrent_multimap( InputIterator first, InputIterator last, const key_compare comp key_compare(), const allocator_type alloc allocator_type() ); template typename InputIterator concurrent_multimap( InputIterator first, InputIterator last, const allocator_type alloc allocator_type() );语义说明构造一个包含半开区间[first, last)内全部元素的concurrent_multimap。可选的comp用于所有键比较可选的alloc用于内存分配。RequirementsInputIterator类型必须满足 ISO C 标准[input.iterators]一节中InputIterator输入迭代器的要求即支持*it、it、it ! last等操作。std::vector迭代器、std::array指针、std::istream_iterator等均可满足。3.2 初始化列表构造concurrent_multimap( std::initializer_listvalue_type init, const key_compare comp key_compare(), const allocator_type alloc allocator_type() ); concurrent_multimap( std::initializer_listvalue_type init, const allocator_type alloc );语义说明这两个版本分别等价于concurrent_multimap(init.begin(), init.end(), comp, alloc)与concurrent_multimap(init.begin(), init.end(), alloc)——即内部直接复用迭代器区间构造函数。由于是multimapinit中即使包含多个键相等的元素也会全部被插入容器不会互相覆盖。3.3 配套的推导指引Deduction Guides借助 C17 的类模板实参推导CTAD上述构造函数还配有 4 条隐式推导指引实现在 concurrent_map.h由__TBB_CPP17_DEDUCTION_GUIDES_PRESENT宏控制仅在支持 C17 推导指引的编译器下启用template typename It, typename Comp std::lessiterator_key_tIt, typename Alloc tbb::tbb_allocatoriterator_alloc_pair_tIt, ... concurrent_multimap( It, It, Comp Comp(), Alloc Alloc() ) - concurrent_multimapiterator_key_tIt, iterator_mapped_tIt, Comp, Alloc; template typename Key, typename T, typename Comp std::lessstd::remove_const_tKey, ... concurrent_multimap( std::initializer_liststd::pairKey, T, Comp Comp(), Alloc Alloc() ) - concurrent_multimapstd::remove_const_tKey, T, Comp, Alloc; // 以及仅带 Alloc 参数的另外两条变体这意味着你可以直接写std::vectorstd::pairint, std::string src{ {1, a}, {2, b} }; oneapi::tbb::concurrent_multimap m(src.begin(), src.end()); // 自动推导 Keyint, Tstd::string oneapi::tbb::concurrent_multimap m2{ {1, a}, {1, b}, {2, c} }; // 等键元素 1 的两个条目都会保留推导指引会从迭代器的value_type或初始化列表的std::pairKey, T中自动提取Key与T并对Key去除const默认比较器为std::less默认分配器为tbb::tbb_allocator。四、拷贝构造函数concurrent_multimap( const concurrent_multimap other ); concurrent_multimap( const concurrent_multimap other, const allocator_type alloc );语义说明构造other的一份完整拷贝新容器中的元素与other中的元素逐一对应。若未显式提供分配器新容器的分配器通过调用std::allocator_traitsallocator_type::select_on_container_copy_construction(other.get_allocator())获得——这是 C 分配器约定的标准做法允许容器在拷贝构造时定制分配器的选择策略例如是否采用与源容器相同或独立的分配器状态。带分配器版本则直接用传入的alloc作为新容器分配器。重要约束拷贝构造期间若对other进行并发操作如其他线程同时向other插入元素则行为未定义。也就是说读取源容器快照这一操作本身必须是独占的。实现层面这两个版本在 concurrent_map.h 中分别声明为 default和委托给base_type(other, alloc)concurrent_multimap( const concurrent_multimap ) default; concurrent_multimap( const concurrent_multimap other, const allocator_type alloc ) : base_type(other, alloc) {}五、移动构造函数concurrent_multimap( concurrent_multimap other ); concurrent_multimap( concurrent_multimap other, const allocator_type alloc );语义说明采用移动语义构造新容器接管other的底层存储跳表节点、已分配内存等通常避免逐元素拷贝的开销。移动后other被置于一个**有效但未指定valid, but unspecified**的状态它可以被正常析构、被重新赋值或重新插入元素但不能假设其内容仍与移动前一致。若未显式提供分配器新容器的分配器通过std::move(other.get_allocator())获得。同样地移动构造期间若对other有并发操作行为未定义。实现上同样通过默认化与基类委托完成见 concurrent_map.hconcurrent_multimap( concurrent_multimap ) default; concurrent_multimap( concurrent_multimap other, const allocator_type alloc ) : base_type(std::move(other), alloc) {}实用提示从源码注释可见显式声明这些构造/析构/赋值成员是为了满足 C 的五法则rule of 5同时兼容隐式推导指引——这也是为什么带分配器版本的拷贝/移动构造会被单独手写而非全部默认化。六、析构函数~concurrent_multimap();语义说明销毁concurrent_multimap依次调用所有已存储元素的析构函数并释放其使用的全部存储空间跳表节点内存由容器的分配器回收。元素析构顺序未在本规范中明确约定依赖析构顺序的业务逻辑不应假设特定次序。重要约束析构期间若对*this自身进行并发操作其他线程正在读写该容器行为未定义。容器被析构时所有指向其元素的迭代器、引用与指针都会失效。七、赋值运算符7.1 拷贝赋值concurrent_multimap operator( const concurrent_multimap other );语义说明用other中所有元素的拷贝替换*this中的全部元素先清空旧内容再逐元素拷贝。分配器的处理遵循标准规则仅当std::allocator_traitsallocator_type::propagate_on_container_copy_assignment::value为true时才执行分配器的拷贝赋值即允许赋值时传播分配器否则保留*this原有的分配器。若对*this与other中的任意一个存在并发操作行为未定义。返回*this的引用支持链式赋值如a b c;。7.2 移动赋值concurrent_multimap operator( concurrent_multimap other );语义说明使用移动语义将other的元素转移至*this替换原有内容other之后处于有效但未指定的状态。分配器处理仅当propagate_on_container_move_assignment::value为true时执行分配器的移动赋值。对*this与other存在并发操作时行为未定义。返回*this的引用。7.3 初始化列表赋值concurrent_multimap operator( std::initializer_listvalue_type init );语义说明用init中的元素整体替换*this的全部内容。特殊语义若init中包含多个键相等的元素具体哪一个会被插入是**未指定unspecified**的——这一点与构造函数等键元素全部保留不同需要注意。原因是赋值过程可能经历先清空再插入或就地替换等实现路径规范刻意不为等键元素的取舍做保证。对*this存在并发操作时行为未定义。返回*this的引用。从源码看赋值运算符同样遵循五法则见 concurrent_map.h拷贝赋值与移动赋值默认化初始化列表版本则显式委托给基类concurrent_multimap operator( const concurrent_multimap ) default; concurrent_multimap operator( concurrent_multimap ) default; concurrent_multimap operator( std::initializer_listvalue_type il ) { base_type::operator (il); return *this; }八、并发安全边界速查规范文档反复强调的未定义行为条款可以汇总成一张速查表这是使用concurrent_multimap时最需要牢记的规则操作并发限制说明拷贝构造other不可并发操作需要读源容器快照移动构造other不可并发操作需要接管源容器存储析构*this不可并发操作销毁期间容器必须完全空闲拷贝赋值*this与other均不可并发操作双向读写移动赋值*this与other均不可并发操作双向读写初始化列表赋值*this不可并发操作整体替换可以这样理解concurrent_multimap的并发安全能力仅覆盖插入、查找、遍历这类细粒度操作由底层并发跳表保证见 concurrent_map.h 引入的detail/_concurrent_skip_list.h而**容器的整体生命周期操作构造、析构、拷贝、赋值**本质上是整容器级的独占操作规范将其明确排除在并发安全承诺之外。编写多线程代码时请务必用外部同步手段如互斥锁保护这些生命周期操作。九、验证途径仓库内测试与配套规范类模板完整成员清单构造函数之外还包括迭代器、查找、安全/不安全修改器、并行遍历等可参考 concurrent_multimap 类规范页。相邻规范页面 safe_modifiers.rst、unsafe_modifiers.rst、deduction_guides.rst 分别定义了并发安全/非安全的元素修改接口与推导指引可与本文的生命周期语义配合阅读。仓库内置的一致性测试 conformance_concurrent_map.cpp 与 test_concurrent_map.cpp 覆盖了concurrent_map/concurrent_multimap的构造、复制与修改行为可作为验证本文章节所述语义的实测参考。十、总结concurrent_multimap的生命周期管理可以提炼为三个要点构造方式丰富空构造可带比较器/分配器、迭代器区间构造、初始化列表构造均可直接使用C17 下还能借助推导指引省略模板参数。拷贝与移动语义遵循 C 标准惯例分配器通过select_on_container_copy_construction与propagate_on_container_*_assignment两个特性控制传播移动后的源容器处于有效但未指定状态。并发边界清晰只有插入、查找、遍历是并发安全的一切涉及容器整体生命周期构造、析构、赋值、拷贝的操作若与并发访问重叠即为未定义行为——这是规范反复强调、源码注释也印证的红线。只要把握住这三条你就能在多线程程序中安全地驾驭这一允许重复键的并发有序容器充分发挥其基于并发跳表的高吞吐插入与查找能力。【免费下载链接】moldmold: A Modern Linker 项目地址: https://gitcode.com/GitHub_Trending/mo/mold创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门