尧图精选

mold 中的线程安全实践:解读 oneTBB Thread Safety 规范及其在链接器中的应用

🕒 发布时间:2026/9/15 12:07:35 📁 来源:尧图网络
mold 中的线程安全实践解读 oneTBB Thread Safety 规范及其在链接器中的应用【免费下载链接】moldmold: A Modern Linker 项目地址: https://gitcode.com/GitHub_Trending/mo/mold导读本文以当前仓库中 third-party/tbb/doc/main/specification/source/thread_safety.rst 文档为骨架系统讲解 oneAPI Threading Building BlocksoneTBB线程安全模型的两条核心规则并结合本仓库中 oneTBB 在 mold 链接器src里的真实应用场景深入解析不同对象可并发、同一对象须互斥的规则如何落地。读完本文你将掌握 oneTBB 线程安全契约的准确含义、并发容器等例外情况的使用边界以及如何从 mold 源码 中验证并运用这些规则写出无数据竞争的并行代码。线程安全规则的默认契约thread_safety.rst 开篇即定义 oneTBB 库的默认线程安全约定——除非类描述中另有说明否则以下两条规则适用于库中的所有方法或函数两个线程可以在不同的对象上并发调用同一个方法或函数两个线程在同一对象上并发调用方法或函数是不安全的。简而言之默认情况下 oneTBB 的线程安全边界是对象级而非函数级的。规则允许同一段代码被多个线程各自作用于自己持有的对象实例上并行执行但不保证同一实例内部状态被多个线程同时触碰时的正确性。这与 C 标准库容器不同容器实例之间互不干扰、同一容器实例的并发读写是数据竞争的模型一脉相承也是并行编程中状态隔离思想的直接体现。例外声明机制文档明确指出Departures from this convention are noted in the classes descriptions即所有偏离上述默认约定的类都会在各自的类描述中显式标注。这意味着阅读 oneTBB 任何一个类时应当遵循先查默认规则再看是否有例外标注的流程而不是想当然地认为某个接口天然支持并发。并发容器更宽松的例外文档紧接着给出最典型的例外——并发容器concurrent containers。由于它们的本质设计目标就是支持多线程对同一个容器对象执行某些并发操作因此其线程安全约定更自由more liberal。从 containers.rst 可以看到 oneTBB 并发容器家族覆盖了完整的容器类型谱系序列Sequencesconcurrent_vector队列Queuesconcurrent_queue、concurrent_bounded_queue、concurrent_priority_queue无序关联容器Unordered associativeconcurrent_hash_map、concurrent_unordered_map、concurrent_unordered_multimap、concurrent_unordered_set、concurrent_unordered_multiset有序关联容器Ordered associativeconcurrent_map、concurrent_multimap、concurrent_set、concurrent_multiset辅助类Auxiliarytbb_hash_compare、node_handles需要注意的是更自由不等于所有操作都并发安全。以concurrent_hash_map为例其类文档在 concurrent_hash_map_cls.rst 中明确区分了并发安全修饰器与并发不安全修饰器两类成员// Defined in header oneapi/tbb/concurrent_hash_map.h namespace oneapi { namespace tbb { template typename Key, typename T, typename HashCompare tbb_hash_compareKey, typename Allocator tbb_allocatorstd::pairconst Key, T class concurrent_hash_map { public: using key_type Key; using mapped_type T; using value_type std::pairconst Key, T; // 并发安全的查找与插入 bool find( const_accessor result, const key_type key ) const; bool find( accessor result, const key_type key ); bool insert( const_accessor result, const key_type key ); bool insert( accessor result, const key_type key ); template typename... Args bool emplace( accessor result, Args... args ); bool erase( const key_type key ); // 并发不安全的整体操作 void clear(); void swap( concurrent_hash_map other ); void rehash( size_type sz 0 ); size_type size() const; size_type bucket_count() const; // ... }; }} // namespace oneapi::tbb其中find、insert、emplace、erase这类针对单个键的细粒度操作才是文档所说的同一容器对象上的某些并发操作而clear()、swap()、rehash()、size()等结构性操作仍然遵循默认规则不允许与其它操作并发执行。这正是并发容器更自由的准确边界。accessor 机制并发访问的钥匙concurrent_hash_map支持并发读写的关键设计是 accessor 与 const_accessor 这一对内嵌类。它们的作用是让多个线程同时、安全地访问容器中的键值对accessor提供读写访问const_accessor提供只读访问一个 accessor 被称为empty当它不指向任何元素通过find/insert/emplace成功命中后accessor 持有对应元素的访问权可通过operator*/operator-解引用release()释放所有权析构函数在非空时也会自动释放。oneapi::tbb::concurrent_hash_mapstd::string, int table; oneapi::tbb::concurrent_hash_mapstd::string, int::accessor acc; if (table.find(acc, key)) { // 获得读写访问权 acc-second 42; // 修改映射值 acc.release(); // 释放访问权 } oneapi::tbb::concurrent_hash_mapstd::string, int::const_accessor cacc; if (table.find(cacc, key)) { // 获得只读访问权 int v cacc-second; }从实现层面看accessor 内部通常持有对应 bucket 的读写锁或引用计数从而保证拿到 accessor 后对元素的读改写与其它线程的插入/删除互不干扰——这正是并发容器得以放宽默认线程安全规则的根本原因。值得注意的是访问器机制仅针对单元素操作若需要并发遍历整个容器则应使用range(grainsize)配合并行算法见下文。互斥原语手动实现同一对象互斥当业务逻辑确实需要在同一个对象上执行复合操作、而该对象又不属于并发容器时oneTBB 提供了完整的互斥原语族来兜底见 mutual_exclusion.rstmutex、rw_mutex通用互斥锁与读写锁spin_mutex、spin_rw_mutex自旋锁与自旋读写锁speculative_spin_mutex、speculative_spin_rw_mutex基于硬件事务内存TSX的投机自旋锁queuing_mutex、queuing_rw_mutex公平排队锁避免自旋锁的饿死问题null_mutex、null_rw_mutex空实现用于在模板参数中关闭锁语义这些原语与默认线程安全规则互补规则说同一对象并发调用不安全互斥原语则提供把这种不安全转化为安全的工具——通过对临界区加锁让对同一对象的访问在时间上串行化。选择哪种锁取决于竞争激烈程度、临界区长短与是否容忍线程饿死等权衡这是 oneTBB 文档中一贯的设计哲学给出默认规则再给出按需绕过的机制。在 mold 链接器中的真实落地本文档所讲述的规则并非抽象教条。当前仓库的 mold 链接器以 oneTBB 为并行运行时src 目录中大量#include tbb/...与tbb::调用即为证据其并行阶段严格遵循不同对象可并发、同一对象须互斥的契约。并行遍历不同对象上的并发mold 中大量的并行循环都通过tbb::parallel_for/tbb::parallel_for_each对不同的输入对象如不同的 ObjectFile、InputSection执行同一处理逻辑例如 gc-sections.cctbb::parallel_for_each(ctx.objs, { // 每个线程处理不同的 ObjectFile互不干扰 ... });每个 lambda 只读写自己负责的那个对象实例完全符合规则第一条两个线程可以在不同的对象上并发调用同一方法。类似的模式还出现在 arch-arm32.cc、arch-ppc64v1.cc、gdb-index.cc 等多处。共享结果汇聚需要并发容器的场合当并行结果必须写入同一个共享结构时mold 便切换到并发容器这一例外机制。例如 mapfile.cc 用concurrent_hash_map记录符号归属#include tbb/concurrent_hash_map.h tbb::concurrent_hash_mapInputSectionE *, std::vectorSymbolE * symtab;多个线程同时对该容器执行insert/find正是利用了concurrent_hash_map支持同一对象并发单元素操作的特性。又如 mold.h 中的未定义符号错误汇总tbb::concurrent_hash_mapSymbolE *, std::vectorstd::string undef_errors;以及 gc-sections.cc 中段名到段集合的映射tbb::concurrent_unordered_mapstd::string_view, tbb::concurrent_vectorInputSectionE * map;icf.cc 的等价类合并则使用了concurrent_unordered_multimaptbb::concurrent_unordered_multimapInputSectionE *, InputSectionE * map;这些容器被多个工作线程并发insert、并发遍历正是文档所述并发容器允许对同一容器对象执行某些并发操作的典型落地。读者若想验证其正确性边界可以对照 concurrent_hash_map_cls.rst 中Concurrently unsafe modifiers一节的声明——这些容器在 mold 中的使用均避开了clear/swap/rehash与其它操作的并发组合。线程私有状态enumerable_thread_specific除了并发容器oneTBB 还通过enumerable_thread_specific提供线程私有存储把不同对象并发的规则贯彻到极致每个线程看到的是自己独有的副本天然无竞争。mold 在 icf.cc、passes.cc、output-chunks.cc、thunks.cc 中多处使用例如tbb::enumerable_thread_specifici64 num_classes; // 每个线程独立的计数器每个线程只读写自己的局部副本最后再归并完全绕开了同一对象的竞争问题。归并扫描parallel_scan 的用法output-chunks.cc 与 gdb-index.cc 展示了tbb::parallel_scan配合tbb::blocked_range做前缀和/归并的用法——这是一种以并行方式安全地聚合共享结果的经典模式其结果也印证了oneTBB 的线程安全模型不仅包含锁与容器还包含算法层面的结构化解竞争。实践要点与自查清单综合文档与源码将 oneTBB 线程安全模型落地到自己的并行代码时建议遵循以下要点先默认后例外任何 oneTBB 类先假设同一对象并发不安全再去类文档中查找是否标注了更宽松的约定如并发容器区分细粒度与结构性操作即便使用并发容器也只并发执行文档明确允许的操作如find/insert/emplace/eraseclear/swap/rehash/size等仍须串行用 accessor 做读改写对concurrent_hash_map元素的读-改-写必须通过accessor/const_accessor获得访问权避免检查后修改的 TOCTOU 竞争共享结果交给并发容器或线程私有存储参考 mold 的做法——需要共享写入时用并发容器需要隔离时用enumerable_thread_specific需要结构化解竞争时用parallel_scan等并行算法同一对象的复合操作必须加锁当并发容器无法覆盖需求时从 mutual_exclusion.rst 的互斥原语族中选择合适的锁普通互斥、读写锁、自旋锁或排队锁保护临界区。延伸阅读线程安全总则原文thread_safety.rst并发容器家族总览containers.rstconcurrent_hash_map完整接口concurrent_hash_map_cls.rst 与 accessors.rst互斥原语族mutual_exclusion.rstmold 中的落地实例gc-sections.cc、icf.cc、mapfile.cc、mold.h、output-chunks.cc、gdb-index.cc【免费下载链接】moldmold: A Modern Linker 项目地址: https://gitcode.com/GitHub_Trending/mo/mold创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
上一篇/下一篇内容由系统自动关联 返回资讯列表 →