ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

mold 内嵌 oneTBB 的 concurrent_map 元素访问解析:at 与 operator[] 的语义、异常与源码实现

mold 内嵌 oneTBB 的 concurrent_map 元素访问解析:at 与 operator[] 的语义、异常与源码实现 mold 内嵌 oneTBB 的 concurrent_map 元素访问解析at 与 operator[] 的语义、异常与源码实现【免费下载链接】moldmold: A Modern Linker 项目地址: https://gitcode.com/GitHub_Trending/mo/mold本篇技术指南以 oneTBB 规范文档 element_access.rst 为主体深入解析oneapi::tbb::concurrent_map的两个元素访问接口——at与operator[]它们的签名、返回值、异常语义、对键缺失的不同处理策略以及当前仓库内嵌 oneTBB 源码中的具体实现与测试佐证。读完本文你将能准确区分这两个接口的适用场景理解std::piecewise_construct原地构造的底层机制并能在并发环境下正确选择读改写容器元素的方式。一、背景concurrent_map 与本文档的定位oneapi::tbb::concurrent_map是一个有序关联容器类模板存储唯一键元素支持并发的插入、查找与遍历但不支持并发删除。其类模板概要定义于 concurrent_map_cls.rst核心形态如下namespace oneapi { namespace tbb { template typename Key, typename T, typename Compare std::lessKey, typename Allocator tbb_allocatorstd::pairconst Key, T class concurrent_map { public: using key_type Key; using mapped_type T; using value_type std::pairconst Key, T; // ... }; } }容器的每个元素item都是一个value_type即std::pairconst Key, T其中item.first是键、item.second是被映射的值。元素访问Element access这一小节规范了at与operator[]两个成员函数——它们都以键为入参返回对应元素的item.second引用但在键不存在这一关键路径上采取了截然不同的策略。在 mold 项目中oneTBB 以 third-party 形式内嵌于 third-party/tbb 目录真实的头文件实现位于 third-party/tbb/include/oneapi/tbb/concurrent_map.h底层数据结构是 third-party/tbb/include/oneapi/tbb/detail/_concurrent_skip_list.h 中定义的concurrent_skip_list跳表第 270 行concurrent_map即concurrent_skip_list的一个特化封装见 concurrent_map.h 第 71-73 行。二、at只读元素访问键缺失即抛异常规范文档给出的两个重载签名如下value_type at( const key_type key ); const value_type at( const key_type key ) const;返回值对键与key等价的元素item返回item.second的引用。抛出的异常若容器中不存在键与key等价的元素抛出std::out_of_range异常。2.1 源码级实现当前仓库内嵌 oneTBB 中at的实现位于 concurrent_map.h 第 113-124 行mapped_type at(const key_type key) { iterator it this-find(key); if (it this-end()) { throw_exception(exception_id::invalid_key); } return it-second; } const mapped_type at(const key_type key) const { return const_castconcurrent_map*(this)-at(key); }实现要点at先调用并发安全的find定位元素若未命中it end()即抛出异常绝不修改容器。抛出的异常经由throw_exception(exception_id::invalid_key)触发invalid_key是 oneTBB 内部异常枚举之一定义于 third-party/tbb/include/oneapi/tbb/detail/_exception.h 第 39 行同文件第 35 行还列出了out_of_range枚举项。从规范与实现的对应关系看该invalid_key异常承载的就是规范文档所述的std::out_of_range语义。const 版本通过const_cast委托非 const 版本实现避免重复代码——由于at在语义上是纯只读操作命中时仅返回引用这种委托在逻辑上是安全的。2.2 测试佐证仓库的关联容器一致性测试 third-party/tbb/test/common/concurrent_associative_common.h 第 106-109 行 覆盖了at的两个重载// mapped_type at( const key_type k ); // const mapped_type at(const key_type k) const; REQUIRE_MESSAGE( cont.at( 1 ) 2, Concurrent container value incorrect ); REQUIRE_MESSAGE( ccont.at( 1 ) 2, Concurrent container value incorrect );其中cont是非 const 容器、ccont是 const 容器分别验证可变与只读两个版本的返回值。专门的元素访问测试函数TestSquareBracketsAndAt位于同一文件 第 1028-1035 行。三、operator[]读改写二合一键缺失时原地插入规范文档给出的两个重载签名如下value_type operator[]( const key_type key ); value_type operator[]( key_type key );语义若容器中不存在键与key等价的元素则插入一个新元素该元素原地构造自std::piecewise_construct, std::forward_as_tuple(key), std::tuple()即等价于构造value_type(key, T())——键为key、映射值为默认构造的T。要求value_type必须满足 ISO C 标准 [container.requirements] 一节定义的EmplaceConstructible要求直观地说映射类型T需要能够被默认构造因为缺失键时会凭空创建一个空值。返回值对键与key等价的元素item返回item.second的引用。3.1 源码级实现当前仓库内嵌 oneTBB 中operator[]的实现位于 concurrent_map.h 第 126-142 行mapped_type operator[](const key_type key) { iterator it this-find(key); if (it this-end()) { it this-emplace(std::piecewise_construct, std::forward_as_tuple(key), std::tuple()).first; } return it-second; } mapped_type operator[](key_type key) { iterator it this-find(key); if (it this-end()) { it this-emplace(std::piecewise_construct, std::forward_as_tuple(std::move(key)), std::tuple()).first; } return it-second; }两个重载的唯一区别在键的处理方式左值重载operator[](const key_type key)std::forward_as_tuple(key)按引用转发左值键不发生拷贝移动右值重载operator[](key_type key)std::forward_as_tuple(std::move(key))将键移动进新元素省去一次拷贝。3.2 原地构造机制piecewise_construct 详解emplace配合std::piecewise_construct是一种分段构造惯用法std::piecewise_construct作为一个哨兵常量指示pair的两个成员分别用各自独立的参数元组构造从而避免为std::pairconst Key, T引入额外的临时对象。std::forward_as_tuple(key)为键成员准备参数{key}std::tuple()为映射值成员准备空参数列表即调用T的默认构造函数。这解释了为何operator[]要求value_type满足EmplaceConstructible当键缺失时映射值必须能够以零参数构造出一个默认值插入容器随后返回其引用供调用方就地写入。这与std::map的operator[]行为一致但 oneTBB 通过piecewise_construct保证插入过程是原地构造的不产生中间value_type临时对象。3.3 注意operator[] 没有 const 版本规范与实现中operator[]均只提供非 const 重载——因为键缺失时它会修改容器插入新元素这与 const 语义根本冲突。需要只读访问时应使用at、find或contains而不是operator[]。四、at 与 operator[]一张表看清差异维度at(key)operator[](key)键缺失时的行为抛出异常容器保持不变原地插入默认构造的新元素并返回其引用是否修改容器否纯只读是可能插入新元素返回类型mapped_type/const mapped_typemapped_type无 const 版本抛异常键不存在时抛出invalid_key语义即std::out_of_range仅在内存分配等底层操作失败时可能抛出前置要求无value_type满足EmplaceConstructible映射类型可默认构造const 容器上可用可用不可用典型场景读取必须已存在的配置项、状态字段计数器累加、缓存填充等不存在则初始化的读改写从实现结构看两者的第一步都是并发安全的findoperator[]在未命中时额外走并发安全的emplaceconcurrent_map.h 第 130 行因此这两个接口整体上继承了底层concurrent_skip_list查找与插入操作的并发安全性。需要特别留意的并发语义是多个线程同时对同一缺失键调用operator[]时各自都会尝试构造一个默认值并emplace最终只有一个元素胜出其余线程拿到的是那个胜出元素的引用——由于emplace的原子性容器状态始终一致但谁的值被写入带有竞争性业务上应先确认这一语义是否符合需求。五、实践建议与常见问题5.1 何时用 at何时用 operator[]若业务约定键必然存在或者键缺失属于程序错误、应当快速暴露使用at——它把键不存在转化为显式异常便于调试与兜底若业务希望首次访问时自动初始化默认值如统计计数、按需缓存的表项使用operator[]——一次调用完成查询或插入 返回引用若需要精确区分键是否存在应改用findcount/contains组合而不是依赖at的异常流或operator[]的隐式插入副作用。5.2 常见问题Q为什么at的 const 版本要通过const_cast委托非 const 版本实现这是 oneTBB 的实现细节concurrent_map.h 第 122-124 行两个版本逻辑完全相同委托实现可以避免重复。由于at命中路径不修改容器const 版本调用非 const 版本不会产生实际的可变副作用这种写法是安全的。Qoperator[]为什么要求映射类型可默认构造因为键缺失时要构造std::piecewise_construct, std::forward_as_tuple(key), std::tuple()其中映射值部分使用空参数元组即调用T()。若T不可默认构造该表达式不合法编译期就会报错——这正对应规范中EmplaceConstructible要求来自 ISO C [container.requirements] 章节。Q容器中已有该键时operator[]会覆盖现有值吗不会。实现先find只有it end()键不存在时才emplace键已存在时直接返回现有元素的item.second引用由调用方决定是否写入新值。Qat与operator[]在并发场景下安全吗从实现结构看两者都建立在底层find与emplace之上而这两个操作属于容器声明支持的并发插入、查找范畴同一容器上并发的at读取与其他写入操作可以安全共存。但operator[]的多线程同键首次访问竞争语义已在第四节说明应结合业务判断是否可接受。六、延伸阅读路径规范正文element_access.rst类模板完整概要构造、修饰符、查找、并行迭代等concurrent_map_cls.rst头文件实现third-party/tbb/include/oneapi/tbb/concurrent_map.h底层数据结构concurrent_skip_listthird-party/tbb/include/oneapi/tbb/detail/_concurrent_skip_list.h异常枚举定义third-party/tbb/include/oneapi/tbb/detail/_exception.h一致性测试operator[]/at覆盖third-party/tbb/test/common/concurrent_associative_common.h、third-party/tbb/test/conformance/conformance_concurrent_map.cpp【免费下载链接】moldmold: A Modern Linker 项目地址: https://gitcode.com/GitHub_Trending/mo/mold创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表