You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

std::future析构的开销是多少?为何示例(a)(b)表现不符预期?

关于std::future销毁开销与async示例的行为差异分析

一、std::future的销毁开销

  • 销毁std::future句柄本身的开销可以忽略:它是一个轻量级对象,内部通常仅包含引用计数或指向共享状态的指针,销毁操作基本是O(1)的简单资源释放。
  • 真正需要关注的是关联任务的阻塞行为:当std::future由std::async以默认启动策略创建,且对应的异步任务尚未完成时,销毁这个std::future会阻塞当前线程,直到任务执行结束。这才是影响程序行为和性能的核心“开销”,而非句柄销毁本身的成本。

二、示例(a)与(b)的核心行为差异

先看你提到的代码示例:

// Example 1
// (a)
{
  async( []{ f(); } ); 
  async( []{ g(); } ); 
}
// (b)
{
  auto f1 = async( []{ f(); } );
  auto f2 = async( []{ g(); } );
}

两者的行为差异源于std::future的销毁时机:

  • 示例(a):每次std::async返回的是临时std::future对象,会在当前语句执行完毕后立即销毁。根据C++标准,默认策略下临时std::future销毁时会阻塞,等待对应任务完成。因此(a)中f()会完整执行结束后,才会启动g(),最终是串行执行。
  • 示例(b):f1和f2是局部变量,会在代码块结束时才被销毁。这意味着两个std::async任务有机会被调度为并行执行(只要系统有足够的线程资源),直到代码块结束时,才会依次等待两个任务完成(局部变量销毁顺序通常是逆序创建顺序,即先销毁f2再销毁f1)。

三、测试结果不符合预期的可能原因

你在测试中看到结果和预期相反,大概率是以下因素导致:

  1. 任务过于轻量:如果f()和g()是极简单的操作(比如空函数、几行算术计算),操作系统来不及调度并行执行,即使是(b)示例也会表现出串行效果,和(a)的差异无法体现。
  2. 启动策略的实际选择:std::async的默认策略允许编译器在std::launch::async和std::launch::deferred中选择。某些编译器在特定环境下可能默认选择延迟执行,此时无论(a)(b),任务都会在future销毁时才执行,且都是串行。
  3. 环境资源限制:如果测试环境是单核心CPU,即使允许并行执行,任务也只能串行运行,无法体现(b)的并行特性。
  4. 测试方法的局限性:测试平台的单次运行可能受调度波动影响,或者你只测量了整个代码块的总时间,而没有验证任务是否在不同线程并行执行(比如打印线程ID)。

如何验证差异

要准确验证两者的行为差异,可以:

  • 给f()和g()添加明显的耗时操作,比如std::this_thread::sleep_for(std::chrono::milliseconds(100))。
  • 显式指定std::launch::async启动策略,强制异步执行:std::async(std::launch::async, []{ f(); })。
  • 在任务内部打印线程ID,确认是否在不同线程中执行。

内容的提问来源于stack exchange,提问作者Another HM

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 21:05:20