并发编程高性能计算【免费下载链接】oneTBBoneAPI Threading Building Blocks (oneTBB)项目地址https://gitcode.com/gh_mirrors/on/oneTBB点击查看免费下载std::swap风格的自由函数是非受限容器unconstrained containers接口的重要组成oneTBB 的tbb::concurrent_hash_map同样提供了与之对应的非成员swap重载用于在两个并发哈希表之间高效交换全部元素。本文将基于 oneTBB 官方参考文档结合 concurrent_hash_map.h 源码实现与 test_concurrent_hash_map.cpp 测试用例完整讲解非成员swap的签名、等价语义、底层实现机制以及需要特别注意的迭代器失效与分配器约束。非成员 swap 的函数签名oneTBB 在tbb命名空间inline namespacev1对外可见中为非成员交换定义了如下重载见 non_member_swap.rsttemplate typename Key, typename T, typename HashCompare, typename Allocator void swap( concurrent_hash_mapKey, T, HashCompare, Allocator lhs, concurrent_hash_mapKey, T, HashCompare, Allocator rhs );该重载的模板参数与concurrent_hash_map类模板完全一致模板参数含义典型默认值Key键类型用于哈希与比较必填T映射值类型键值对中的 value必填HashCompare哈希与比较器需满足tbb_hash_compare概念tbb_hash_compareKeyAllocator节点分配器类型tbb_allocatorstd::pairconst Key, T值得注意的一个细节是非成员swap要求两个容器的HashCompare与Allocator模板参数完全相同同类型而参考文档中另外提供的非成员二元比较运算符operator、operator!则允许两个容器使用不同的分配器A1、A2。这一点从 concurrent_hash_map.h 的源码结构可以清楚看到swap的非成员版本只接收单一模板参数A而比较运算符接收A1、A2两个分配器参数。语义等价于 lhs.swap(rhs)官方文档明确说明该非成员函数等价于调用成员函数lhs.swap(rhs)。成员版swap的声明位于 concurrent_hash_map.h// swap two instances. Iterators are invalidated void swap(concurrent_hash_map table) { using pocs_type typename node_allocator_traits::propagate_on_container_swap; using is_equal_type typename node_allocator_traits::is_always_equal; swap_allocators(this-my_allocator, table.my_allocator); internal_swap(table, tbb::detail::disjunctionpocs_type, is_equal_type()); }源码中注释“Iterators are invalidated”直接揭示了 swap 最重要的语义特性交换操作会使两个容器的迭代器全部失效。因此swap 之后任何对旧迭代器的解引用都是未定义行为代码中应始终在 swap 后重新获取迭代器。非成员版的实际实现非常简单——它只是对成员版的一层转发template typename Key, typename T, typename HashCompare, typename A inline void swap(concurrent_hash_mapKey, T, HashCompare, A a, concurrent_hash_mapKey, T, HashCompare, A b) { a.swap( b ); }由于这一转发关系非成员swap与成员swap在时间/空间复杂度、异常保证、分配器处理规则上完全一致。在泛型代码中优先使用非成员swap配合using std::swap;的 ADL 惯用法可以让算法自动选择最合适的交换实现这正是该重载存在的意义。底层实现分配器规则与桶表指针交换从源码可以深入理解swap的底层机制。成员swap分两步完成第一步交换分配器。通过swap_allocators(this-my_allocator, table.my_allocator)处理两个容器分配器之间的互换。这一行为受 C 分配器传播规则的约束具体由node_allocator_traits即tbb::detail::allocator_traitsnode_allocator_type见 concurrent_hash_map.h中的两个特性决定propagate_on_container_swapPOCS是否在 swap 时传播分配器is_always_equal分配器是否总是相等对于 oneTBB 默认的tbb_allocator这类无状态分配器该特性通常为true。第二步按特性分派交换内容。内部通过tbb::detail::disjunctionpocs_type, is_equal_type()在编译期选择两条路径见 concurrent_hash_map.hvoid internal_swap(concurrent_hash_map other, /*is_always_equal || POCS */ std::true_type) { this-internal_swap_content(other); } void internal_swap(concurrent_hash_map other, /*is_always_equal || POCS */ std::false_type) { __TBB_ASSERT(this-my_allocator other.my_allocator, nullptr); this-internal_swap_content(other); }两条路径最终都调用internal_swap_content其核心动作是交换内部哈希桶表bucket 数组的指针与掩码mask属于典型的常数时间 O(1) 指针交换不会逐个搬移元素节点。区别仅在于当分配器不保证相等且不支持 POCS 传播时false_type分支运行时会通过__TBB_ASSERT断言两个容器的分配器相等否则触发断言失败。这保证了 swap 后元素始终由正确的分配器管理避免内存所有权错乱。同时因为交换的是一组指向 bucket 数组的指针而非元素本身swap 操作本身是高并发友好的——它不涉及对各个 bucket 的加锁遍历也就不会与正在进行的查找/插入产生逐桶竞争从实现结构看swap 的调用方仍需自行保证与其他写操作之间的同步。分配器行为的实际验证swap在分配器不满足 is_always_equal、且不传播 POCS时的行为在测试用例中有专门覆盖。位于 test_concurrent_hash_map.cpp 的TEST_CASE(swap with NotAlwaysEqualAllocator allocators)使用自定义的“不总是相等”分配器执行TEST_CASE(swap with NotAlwaysEqualAllocator allocators) { // ... 构造带自定义分配器的 map1、map2 ... swap(map1, map2); // ... 验证交换后内容与分配器状态 ... }该测试验证了即使分配器类型内部状态不同is_always_equal false只要满足my_allocator other.my_allocator分配器比较相等swap 依然安全完成。这与上文internal_swap的false_type分支逻辑完全对应——断言通过即执行指针交换。实战用法示例以下示例演示了非成员swap的典型使用方式先填充两个表再通过swap交换内容并验证元素归属正确互换#include oneapi/tbb/concurrent_hash_map.h #include cassert int main() { using map_t tbb::concurrent_hash_mapint, int; map_t a, b; a.emplace(1, 100); a.emplace(2, 200); b.emplace(3, 300); // 非成员 swap等价于 a.swap(b) swap(a, b); // 交换后a 包含 {3,300}b 包含 {1,100}、{2,200} map_t::const_accessor ca; assert(a.find(ca, 3) ca-second 300); assert(b.find(ca, 1) ca-second 100); assert(b.find(ca, 2) ca-second 200); assert(a.size() 1 b.size() 2); return 0; }在泛型算法中更推荐的写法是结合 ADLArgument-Dependent Lookuptemplate typename Container void generic_swap(Container x, Container y) { using std::swap; // 回退到 std::swap swap(x, y); // ADL 优先找到 tbb::swap 重载 }当Container实例化为tbb::concurrent_hash_map时ADL 会优先选中tbb命名空间中的非成员swap重载从而得到 O(1) 的桶表交换语义而不是逐个元素交换。使用注意事项总结迭代器失效swap 会使两个容器的全部迭代器失效源码注释明确标注swap 后必须重新获取迭代器或访问器accessor。引用与指针从实现看swap 交换的是 bucket 数组指针元素节点本身不被搬移因此对单个value_type元素的引用在 swap 后仍然有效但官方参考文档仅承诺迭代器语义实战中以文档承诺为准。模板参数一致性非成员swap要求两个容器的HashCompare与Allocator类型完全相同若类型不同编译期即会报错需改用成员版或自行实现逐元素迁移。分配器约束当分配器is_always_equal false且不支持 POCS 时swap 仅在两个容器分配器比较相等时合法否则触发断言。复杂度swap 是常数时间操作O(1)不复制任何元素适合用于快速“换手”两个并发哈希表的内容。并发语义swap 操作本身不逐桶加锁调用方需确保 swap 期间两个容器不被其他线程并发修改。非成员swap是 oneTBBconcurrent_hash_map容器接口中简洁而重要的一环它以标准std::swap惯用法为用户提供统一交换入口底层则由分配器特性驱动的编译期分派保证安全性配合 O(1) 的桶表指针交换实现高性能换手。理解其签名、等价转发关系与迭代器失效语义能帮助你在并发容器编程中写出既符合标准惯例又安全高效的代码。赞分享并发编程高性能计算【免费下载链接】oneTBBoneAPI Threading Building Blocks (oneTBB)项目地址https://gitcode.com/gh_mirrors/on/oneTBB点击查看免费下载相关推荐oneTBB concurrent_bounded_queue 非成员 swap 解析签名、语义与源码实现oneTBB concurrent_bounded_queue 非成员 swap 解析签名、语义与源码实现 本篇文章围绕 oneTBB 官方 API 参考中的并发编程高性能计算OpenAlice 数据迁移框架与迁移索引指南从 0.89.2-beta 基线到 0044 的版本化状态演进OpenAlice 数据迁移框架与迁移索引指南从 0.89.2 beta 基线到 0044 的版本化状态演进 OpenAliceYour one perso免注册、免 Key免费天气 API Open-Meteo 快速上手指南免注册、免 Key免费天气 API Open Meteo 快速上手指南 给 App 加天气功能第一步常常不是写代码而是注册申请 Key、翻配额表、逐条核后端API网关数据工程上一篇抖音批量下载实战一夜搬回博主274条作品下一篇baidupankey百度网盘提取码查询完整教程贴链接 3 秒出码创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考