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

mold 内嵌 oneTBB 源码解读:concurrent_multiset 的 Observers(get_allocator / key_comp / value_comp)

mold 内嵌 oneTBB 源码解读concurrent_multiset 的 Observersget_allocator / key_comp / value_comp【免费下载链接】moldmold: A Modern Linker 项目地址: https://gitcode.com/GitHub_Trending/mo/mold本篇技术指南以 observers.rst 为骨架完整剖析 oneTBBoneapi::tbb::concurrent_multiset三个观察者Observer成员函数——get_allocator()、key_comp()、value_comp()的接口语义、源码级实现与实战用法。mold 仓库在 third-party/tbb 目录下完整内置了 oneTBB 的实现与规范文档阅读本文后你将能准确理解这三个函数返回副本而非引用的设计意图、它们与构造参数比较器、分配器的绑定关系以及value_comp在 set/multiset 类容器中的特殊实现方式并可直接将示例代码用于你的并发编程项目。concurrent_multiset 与 Observers 在类接口中的位置oneapi::tbb::concurrent_multiset是一个表示有序元素序列的类模板支持并发的插入、查找与遍历但不支持并发删除与concurrent_set不同的是它允许存储多个等价元素。其模板签名在规范文档 concurrent_multiset_cls.rst 中定义如下// Defined in header oneapi/tbb/concurrent_set.h namespace oneapi::tbb { template typename T, typename Compare std::lessT, typename Allocator tbb_allocatorT class concurrent_multiset { public: using key_type T; using value_type T; using key_compare Compare; using value_compare Compare; using allocator_type Allocator; // ... }; }注意三个与观察者密切相关的类型别名由于 multiset 中value_type与key_type同为T因此key_compare与value_compare也声明为同一个类型Compareallocator_type默认是tbb_allocatorT。在类接口的末尾位于Lookup成员之后、Parallel iteration之前规范文档定义了三个观察者成员// Observers key_compare key_comp() const; value_compare value_comp() const; allocator_type get_allocator() const; // 声明于类 synopsis 的构造/析构区之后观察者一词的语义是这些成员函数只查询容器当前关联的策略对象比较器、分配器不修改容器状态也不改变容器内容因此它们都是const成员函数且按值返回副本而非引用。get_allocator()获取容器关联的分配器接口规范allocator_type get_allocator() const;Returns: a copy of the allocator associated with*this返回与*this关联的分配器的一份副本。源码级实现在 mold 内置的 oneTBB 实现中concurrent_multiset继承自concurrent_skip_list跳表容器该函数实现在 _concurrent_skip_list.hallocator_type get_allocator() const { return my_node_allocator; }即直接返回容器内部持有的节点分配器成员my_node_allocator的副本。这解释了三点实现事实返回的是节点分配器的副本跳表容器的每个元素节点的内存都由my_node_allocator分配观察者返回的就是这份分配器的拷贝值语义返回副本意味着调用方得到的是一个独立于容器的分配器对象可在容器之外用它分配/释放同类型对象且该拷贝与容器内部的分配器在多数实现下相等std::allocator_traits::is_always_equal为真时任意两份拷贝恒等与传播策略联动在复制构造时实现通过select_on_container_copy_construction(other.get_allocator())见 _concurrent_skip_list.h决定新容器采用哪个分配器在swap中则依据propagate_on_container_swap与is_always_equal判断是否交换分配器见 _concurrent_skip_list.h。因此get_allocator()是实现分配器感知allocator-aware容器语义的关键入口。使用示例#include oneapi/tbb/concurrent_set.h #include cassert int main() { oneapi::tbb::concurrent_multisetint s; // 默认模板参数下 allocator_type 为 tbb_allocatorint oneapi::tbb::concurrent_multisetint::allocator_type alloc s.get_allocator(); // 多次调用返回的副本彼此相等 assert(alloc s.get_allocator()); // 可以用拿到的分配器在容器之外分配元素大小的内存 int* p alloc.allocate(1); alloc.deallocate(p, 1); return 0; }key_comp()获取键比较器接口规范key_compare key_comp() const;Returns: a copy of the key comparison functor associated with*this返回与*this关联的键比较函数对象的一份副本。key_compare即模板参数Compare默认std::lessT。源码级实现key_compare key_comp() const { return my_compare; }实现在 _concurrent_skip_list.h。其中my_compare是容器持有的比较器成员声明于同一文件 _concurrent_skip_list.h类型为key_compare。它由构造函数接收explicit concurrent_skip_list(const key_compare comp, const allocator_type alloc allocator_type())见 _concurrent_skip_list.h。由此可以推断出两个关键结论key_comp()返回的正是构造时传入的那份比较器或其拷贝因此当你用带状态如引用计数、统计字段的比较器构造容器后可以通过key_comp()取回该状态由于返回的是副本调用方修改返回对象不会影响容器内部的排序行为。使用示例#include oneapi/tbb/concurrent_set.h #include cassert // 降序比较器a 排在 b 之前当且仅当 a b struct ReverseLess { bool operator()(int a, int b) const { return a b; } }; int main() { // 构造时传入比较器实例 oneapi::tbb::concurrent_multisetint, ReverseLess s(ReverseLess{}); // 取回比较器副本并验证排序方向 auto kc s.key_comp(); assert(kc(2, 1)); // 2 应先于 1 assert(!kc(1, 2)); return 0; }规范文档对Compare的要求是满足 ISO C 标准 [alg.sorting] 的Compare要求严格弱序对Allocator的要求是满足 [allocator.requirements] 的Allocator要求见 concurrent_multiset_cls.rst。value_comp()获取值比较器接口规范value_compare value_comp() const;Returns: an object of thevalue_compareclass that is used to comparevalue_typeobjects返回一个value_compare类的对象用于比较value_type对象。源码级实现set/multiset 中与 key_comp 同型这是三个观察者中最值得展开的一点。在std::multiset中value_compare是一个由key_compare派生的嵌套类而在 oneTBB 的 set/multiset 实现中由于value_type就是key_type两者被直接统一为同一个类型。证据在 concurrent_set.h 的set_traits中templatetypename Key, typename KeyCompare, ... struct set_traits { using key_type Key; using value_type key_type; using compare_type KeyCompare; using value_compare compare_type; // value_compare 即 Compare 本身 // ... static value_compare value_comp(compare_type comp) { return comp; } };而concurrent_multiset的value_compare正是base_type::value_compare见 concurrent_set.h底层的value_comp()实现为value_compare value_comp() const { return container_traits::value_comp(my_compare); }见 _concurrent_skip_list.h。结合set_traits::value_comp的实现可知value_comp()与key_comp()返回的是同一比较器——value_comp(my_compare)只是把my_compare原样包装/透传为value_compare类型行为与key_comp()完全一致。使用示例#include oneapi/tbb/concurrent_set.h #include cassert int main() { oneapi::tbb::concurrent_multisetint s; // multiset 中 value_compare 与 key_compare 同型均为 std::lessint auto vc s.value_comp(); assert(vc(1, 2)); assert(!vc(2, 2)); // 严格弱序相等元素不满足小于关系 // 因为 value_type key_typevalue_comp 可直接用于比较元素 oneapi::tbb::concurrent_multisetint::value_type a 1, b 2; assert(vc(a, b)); return 0; }三者共性、调用链与注意事项成员函数返回类型返回值来源源码是否修改容器get_allocator()allocator_typemy_node_allocator副本_concurrent_skip_list.h否key_comp()key_comparemy_compare副本_concurrent_skip_list.h否value_comp()value_comparecontainer_traits::value_comp(my_compare)_concurrent_skip_list.h否使用时的核心注意事项观察者与构造参数一一对应构造时传入的比较器comp存入my_comparekey_comp/value_comp的返回值来源传入的分配器alloc存入my_node_allocatorget_allocator的返回值来源。想验证容器到底用了什么排序规则、什么分配策略这三个函数是唯一的只读查询入口可在空容器上安全调用三者均不访问元素节点只访问策略成员因此对默认构造的空容器调用完全合法常用于先取比较器、再决定插入策略的写法返回副本修改无副作用对返回对象做修改不会反向影响容器内部行为如果需要在容器创建后改变排序规则只能重新构造容器与透明异质查找的关系concurrent_skip_list内部通过is_transparent判断key_compare是否支持异质查找见 _concurrent_skip_list.hkey_comp()返回的比较器正是这些find/count/equal_range异质重载所复用的同一策略对象区别于std::multiset的value_compare标准库中value_compare是嵌套类而 oneTBB 的 set/multiset 直接令value_compare Compare因为二者比较的对象value_type与key_type本就是同一个T——这是从源码结构concurrent_set.h可以明确推断出的实现差异。小结concurrent_multiset的观察者接口虽然只有三个成员函数却是理解整个容器策略注入机制的最小切入点get_allocator()暴露出节点分配器与分配器传播策略key_comp()直接映射到构造参数与跳表节点比较逻辑value_comp()则展示了 set/multiset 家族键值一体类型设计的直接后果。配合 mold 仓库内 observers.rst、concurrent_multiset_cls.rst、concurrent_set.h 与 _concurrent_skip_list.h 交叉阅读即可在规范与实现两个层面完整掌握这些观察者的语义边界进而在自己的并发容器设计中复用同样的策略对象 只读查询模式。【免费下载链接】moldmold: A Modern Linker 项目地址: https://gitcode.com/GitHub_Trending/mo/mold创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

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

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