
transcript
show notes
一次看似简单的 C++ 延迟初始化选择,背后牵出性能、异常语义与并发正确性的完整链条。Raymond Chen 从 `std::async`、`std::shared_future` 与 `std::call_once` 的实际行为出发,解析通用异步抽象用于一次性初始化时的隐藏成本与适用边界。
节目还深入回顾旧版 GCC 的 `call_once` 实现缺陷:仅依赖硬件 acquire/release 并不能阻止编译器对普通访问进行重排。本文不仅解释“该选哪个接口”,更帮助读者建立从语言内存模型、编译器到运行库实现的并发审查视角。建议结合原文阅读,获取完整示例与推导。
原文链接:
https://devblogs.microsoft.com/oldnewthing/20260917-00/?p=112706
原文标题:std::call_once vs. std::async
主要内容:
• `std::future::get()` 是破坏性读取;若将其当作可重复查询的缓存,第二次调用可能触发未定义行为。
• `std::shared_future` 能安全共享结果或异常,但需要共享状态、堆分配、引用计数及任务框架支持,延迟执行不等于没有运行时成本。
• `std::call_once` 更贴合“一次成功初始化并安全发布结果”的需求,通常更轻量;但其异常语义是失败后允许后续调用重试。
• `std::shared_future` 会缓存初始化异常,之后的 `get()` 将重复抛出同一异常;这实质上是在“记住失败”与“允许重试”之间做系统设计选择。
• 旧版 GCC 的案例说明:硬件内存序无法替代语言级同步保障,必须同时审视编译器重排、标准内存模型与实际标准库实现。
推荐理由:
这篇文章把 C++ 并发工具的接口差异落到了真实工程决策:结果怎样共享、异常是否缓存、失败能否重试,以及为通用能力付出的隐性成本。它尤其提醒我们,并发代码不能只凭“底层硬件有内存序”判断正确性;语言、编译器和库实现缺一不可。
---
「Andrej Karpathy的RSS订阅清单」为您精选全球最前沿的AI技术博客文章,深度剖析技术背后的核心洞察。
由 voieech.com 提供技术支持。





