oneapi::tbb::concurrent_multiset 构造、析构与复制语义全解析
oneapi::tbb::concurrent_multiset 构造、析构与复制语义全解析【免费下载链接】moldmold: A Modern Linker 项目地址: https://gitcode.com/GitHub_Trending/mo/moldconcurrent_multiset是 oneAPI Threading Building BlocksoneTBB提供的有序并发容器允许存储多个等价键并支持并发插入、查找与遍历但不支持并发删除。本指南围绕该容器的生命周期管理展开如何创建空容器、如何从迭代器区间或初始化列表批量构建、拷贝/移动构造与赋值的行为细节、以及析构时的清理语义。读完本文你将掌握concurrent_multiset全部构造与赋值重载的正确用法理解其底层基于并发跳跃表skip list的实现原理并能避开“与容器并发操作时行为未定义”这一经典陷阱。概述一个允许重复键的有序并发容器concurrent_multiset是concurrent_set的多重multiset版本二者共享同一套类模板骨架仅在多映射AllowMultimapping开关上不同。它的完整类模板声明位于 oneapi/tbb/concurrent_set.hnamespace oneapi { namespace tbb { template typename T, typename Compare std::lessT, typename Allocator tbb_allocatorT class concurrent_multiset { public: using key_type T; using value_type T; // ... }; } }T元素类型同时充当键key_type value_type TCompare默认std::lessT必须满足 ISO C 标准 [alg.sorting] 一节对Compare的要求用于维护元素的严格弱序Allocator默认tbb_allocatorT必须满足 [allocator.requirements] 一节对Allocator的要求且std::allocator_traitsAllocator::destroy(m, val)必须良构well-formed。从源码结构看concurrent_multiset 类 通过using base_type::base_type;直接继承底层concurrent_skip_listset_traitsKey, Compare, geometric_level_generator32, Allocator, true的全部构造与析构能力只额外补写了拷贝/移动赋值与 initializer_list 赋值重载以符合 Rule of 5。这意味着本文讨论的所有构造行为最终都在 detail/_concurrent_skip_list.h 的concurrent_skip_list中落地实现。空容器构造函数concurrent_multiset(); explicit concurrent_multiset( const key_compare comp, const allocator_type alloc allocator_type() ); explicit concurrent_multiset( const allocator_type alloc );三个重载均构造一个空concurrent_multiset默认构造不提供任何比较器与分配器使用Compare的默认实例和allocator_type()默认分配器提供comp时后续所有key_type比较均使用该比较函数对象提供alloc时所有节点内存都由该分配器分配。对应底层实现位于 _concurrent_skip_list.h默认构造将头指针my_head_ptr置空、最大高度my_max_height置 0带比较器与分配器的重载则先初始化节点分配器my_node_allocator(alloc)、比较器my_compare(comp)再完成同样的空初始化仅带分配器的重载通过委托concurrent_skip_list(key_compare(), alloc)实现。从元素序列构造从迭代器区间构造template typename InputIterator concurrent_multiset( InputIterator first, InputIterator last, const key_compare comp key_compare(), const allocator_type alloc allocator_type() ); template typename InputIterator concurrent_multiset( InputIterator first, InputIterator last, const allocator_type alloc allocator_type() );构造一个包含半开区间[first, last)内全部元素的容器。InputIterator必须满足 ISO C 标准 [input.iterators] 一节对输入迭代器的要求。底层实现在 _concurrent_skip_list.h 中先以比较器与分配器构造空容器再调用internal_copy(first, last)逐元素insert(*it)。由于是 multiset区间内的重复键会被完整保留。从初始化列表构造concurrent_multiset( std::initializer_listvalue_type init, const key_compare comp key_compare(), const allocator_type alloc allocator_type() ); concurrent_multiset( std::initializer_listvalue_type init, const allocator_type alloc );两个重载分别等价于concurrent_multiset(init.begin(), init.end(), comp, alloc)与concurrent_multiset(init.begin(), init.end(), alloc)见 _concurrent_skip_list.h。{1, 2, 3, 4, 4}这类含重复元素的列表可以直接用于构造 multiset重复键全部保留。拷贝构造函数concurrent_multiset( const concurrent_multiset other ); concurrent_multiset( const concurrent_multiset other, const allocator_type alloc );未显式提供分配器时新容器的分配器通过std::allocator_traitsallocator_type::select_on_container_copy_construction(other.get_allocator())获得提供alloc的重载直接用该分配器同时拷贝比较器my_compare(other.my_compare)元素通过internal_copy(other)逐项插入_concurrent_skip_list.h并带有__TBB_ASSERT(size() other.size())断言检查拷贝完整性异常安全internal_copy内部使用try_call包装若拷贝过程中插入抛出异常会回滚清理已插入节点并重新抛出_concurrent_skip_list.h。注意拷贝构造期间若other被其他线程并发修改行为未定义。因为底层跳跃表只保证并发安全的插入/查找/遍历不保证拷贝过程中容器保持稳定这一前提。移动构造函数concurrent_multiset( concurrent_multiset other ); concurrent_multiset( concurrent_multiset other, const allocator_type alloc );未显式提供分配器时分配器通过std::move(other.get_allocator())获得other被移入后处于有效但未指定valid but unspecified的状态仍可安全析构或重新赋值底层internal_move以宽松内存序搬运my_head_ptr与my_max_height并把other的头指针清空、最大高度归零实现指针级转移而非逐元素拷贝_concurrent_skip_list.h带分配器的移动构造分两种情况_concurrent_skip_list.h若分配器is_always_equal为真或与新分配器相等则直接internal_move转移所有权否则退化为基于std::make_move_iterator的逐元素移动拷贝因为节点内存归属不同分配器无法整体交接。析构函数~concurrent_multiset();销毁容器调用所有已存元素的析构函数并释放其使用的存储。底层实现在 _concurrent_skip_list.h 中析构先clear()释放全部数据节点再delete_head()释放哨兵头节点。同样地析构期间对*this的任何并发操作行为未定义。赋值运算符拷贝赋值concurrent_multiset operator( const concurrent_multiset other );用other中元素的拷贝替换*this中的全部元素返回*this引用。当std::allocator_traitsallocator_type::propagate_on_container_copy_assignment::valuePOCA为true时还会拷贝赋值分配器。底层实现_concurrent_skip_list.h先做自赋值防护this ! other随后clear()旧内容、通过copy_assign_allocators处理分配器传播、拷贝比较器并重新internal_copy。*this与other上存在并发操作时行为未定义。移动赋值concurrent_multiset operator( concurrent_multiset other );用other的元素以移动语义替换*thisother保持有效但未指定状态。当propagate_on_container_move_assignment::valuePOMA为true时移动赋值分配器。底层实现_concurrent_skip_list.h先清空并释放旧头节点再根据propagate_on_container_move_assignment与is_always_equal的析取结果决定是整体转移所有权还是逐元素移动。*this与other存在并发操作时行为未定义。初始化列表赋值concurrent_multiset operator( std::initializer_listvalue_type init );用init的元素替换*this的全部内容。若init中存在多个等价键插入其中哪一个元素不做保证未指定。底层实现_concurrent_skip_list.h即clear()后调用insert(il.begin(), il.end())。同样要求赋值期间*this无并发操作。拷贝/移动语义速查表操作签名分配器行为并发约束默认构造concurrent_multiset()默认分配器—比较器分配器构造explicit concurrent_multiset(comp, alloc {})指定分配器—迭代器区间构造concurrent_multiset(first, last, comp, alloc)指定分配器—初始化列表构造concurrent_multiset(init, comp, alloc)指定分配器—拷贝构造concurrent_multiset(const concurrent_multiset other)select_on_container_copy_construction拷贝期间other禁止并发操作带分配器拷贝构造concurrent_multiset(other, alloc)显式指定同上移动构造concurrent_multiset(concurrent_multiset other)std::move(other.get_allocator())移动期间other禁止并发操作带分配器移动构造concurrent_multiset(other, alloc)显式指定分配器不相等时退化为逐元素移动拷贝同上析构~concurrent_multiset()释放全部存储析构期间*this禁止并发操作拷贝赋值operator(const concurrent_multiset other)POCA 为真时传播*this与other均禁止并发操作移动赋值operator(concurrent_multiset other)POMA 为真时传播同上初始化列表赋值operator(std::initializer_list init)使用既有分配器*this禁止并发操作实践建议与常见陷阱构造期并发操作的风险所有构造、析构、赋值操作在底层都执行遍历 逐节点插入/删除的非原子过程因此任何容器被构造/析构/赋值的同时又被其他线程访问的模式都会触发未定义行为。正确的并发使用姿势是先单线程完成构造必要时配合insert随后再进入多线程阶段执行查找与插入。利用迭代器区间构造做批量初始化与其在并发阶段逐个insert不如预先在std::vector中收集元素再用concurrent_multiset(v.begin(), v.end())一次性构建减少并发阶段的竞争窗口。multiset 与 set 的选择允许重复键时使用concurrent_multiset需要键唯一性时使用concurrent_set。两者模板形参完全一致可无缝替换。C17 类模板实参推导concurrent_multiset支持显式推导指引见 deduction_guides.rstoneapi::tbb::concurrent_multiset cs1(v.begin(), v.end());可直接推导为concurrent_multisetint无需手写模板实参。推导指引在源码中以__TBB_CPP17_DEDUCTION_GUIDES_PRESENT宏保护concurrent_set.h。测试验证oneTBB 的 conformance 测试 conformance_concurrent_set.cpp 覆盖了concurrent_multiset的移动语义、initializer_list 构造含{1, 2, 3, 4, 4}重复键场景与节点句柄等接口可作为行为对照的参考实现验证。参考资料类模板总览与完整成员清单concurrent_multiset_cls.rst本文主题构造、析构、复制官方规范原文construction_destruction_copying.rst类模板推导指引deduction_guides.rst底层跳跃表实现detail/_concurrent_skip_list.h类定义与推导指引声明oneapi/tbb/concurrent_set.h【免费下载链接】moldmold: A Modern Linker 项目地址: https://gitcode.com/GitHub_Trending/mo/mold创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联
返回资讯列表 →