当前位置: 首页 > news >正文

条款38:注意线程句柄析构函数的各种不同行为

1 线程句柄

  1. std::thread对象和期值对象都可以被视为系统线程的句柄。然而它们的析构函数中的行为却如此不同:
    1)可join的std::thread的销毁会终止程序,因为隐式join和隐式detach——被认为是更糟糕的选择。
    2)期值的销毁从不会导致程序终止,有时表现得好像它做了一个隐式join,有时又好像做了一个隐式detach,有时则两者都不是。
    在这里插入图片描述
  2. 但是被调用者的结果存储在哪里呢?
    1)不能存储在被调用者的std::promise中:被调用者可能在调用者调用相应的std::future的get方法之前就完成了。由于该对象是被调用者本地的,当被调用者完成时,它将被销毁。
    2)不能存储在调用者的期值中:因为std::future可能用于创建std::shared_future,结果必须至少存在与最后一个引用它的期值一样长的时间,无法确定由哪个期值来包含其结果(也许结果的类型是只能移动的,不能被拷贝)。
    //共享状态通常由基于堆的对象表示,但其类型、接口和实现并未由标准指定。标准库的作者可以自由地以任何方式实现共享状态。
    在这里插入图片描述
  3. 共享状态的存在很重要,因为期值析构函数的行为是由与其关联的共享状态决定的:
    1)经由 std::async 启动的未推迟任务,指涉到的共享状态的最后一个期值会保持阻塞,直至该任务结束。本质上,这样一个期值的析构函数是对底层异步执行任务的线程实施了一次隐式 join。
    2)其他所有期值对象的析构函数只将期值对象析构就结束了。对于底层异步运行的任务,这样做类似于对线程实施了一次隐式 detach。如果这一期值是最后一个,也就意味着被推迟的任务将不会有机会运行了。
  4. 规则其实很简单。要处理的只是简单的“正常”行为和一个例外。
    1)“正常”行为是指,期值的析构函数会销毁期值对象本身。它只是销毁期值的数据成员(以及减少共享状态中的引用计数)。
    2)例外:阻塞,直到异步运行的任务完成。实际上,这相当于与运行std::async创建的任务的线程进行隐式join。
  5. 只有当所有下面这些条件都满足时,期值的析构函数才会表现出例外行为:
    1)它指涉到由于调用 std::async 而创建的共享状态。
    2)任务的启动策略是 std::launch::async:这可能是由运行时系统选择的,也可能是在调用 std::async 时指定的。
    3)它是最后一个指向共享状态的期值:对于 std::future,这一点总是成立的。对于 std::shared_future,如果还有其他 std::shared_future 也指向相同的共享状态,那么它只是销毁其数据成员。
  6. 期值的 API 无法确定它是否涉及到由调用std::async产生的共享状态,所以对于任意的期值对象,无法知晓它是否会在其析构函数中阻塞(等待异步运行的任务完成):
// 容器可能会在其析构函数中阻塞
// 因为它包含的期值可能涉及通过std::async启动的非延迟任务的共享状态
std::vector<std::future<void>> futs; class Widget { // Widget 对象可能会在其析构函数中阻塞
public:private:std::shared_future<double> fut;
};

解决方法之一是使用std::packaged_task,std::packaged_task对象通过包装函数(或其他可调用对象),以便将其结果放入共享状态。可以通过std::packaged_task的get_future函数获取指向该共享状态的期值:

int calcValue();				//要运行的函数
std::packaged_task<int()> pt(calcValue);	//包装 calcValue 函数,使其可以异步运行
auto fut = pt.get_future();//获取指向共享状态的期值

std::packaged_task是不可拷贝的,所以当pt传递给std::thread构造函数时,它必须被强制转换为右值:

{ // 块开始std::packaged_task<int()> pt(calcValue);auto fut = pt.get_future();std::thread t(std::move(pt));// 见下文
} // 结束块

有趣的是t在”…”区域内会发生什么:
1)t不发生任何变化:t在作用域的末尾是可join的。这会导致项目终止。
2)对t执行了join:fut不需要在其析构函数中阻塞,因为调用fut的代码已经进行了join。
3)对t执行了detach:fut不需要在其析构函数中进行分离,因为调用fut的代码已经进行了分离。

2 要点速记

  1. 期值的析构函数通常只销毁对象的数据成员。
  2. 涉及到通过std::async启动的非延迟任务的共享状态的最后一个期值,会阻塞直到任务完成(相当于隐式join)。
http://www.dtcms.com/a/578641.html

相关文章:

  • 2027秋招备战day7
  • 嵌入式Linux C语言程序设计七
  • 学校让做网站做完怎么交全球最受欢迎的网站
  • 网站建设的报价单网络架构需求
  • 离心萃取机:破解磷酸萃取全流程痛点,筑牢高效生产核心
  • 测开学习DAY24
  • 【Java EE进阶 --- SpringBoot】统一功能处理
  • python--标准库os
  • 写网站建设需求株洲手机网站建设
  • 8图片这样的网站怎么做的新手这样开办公司
  • Springboot的家庭理财系统00sic864(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。
  • 从开发到合并:AICR 项目 Git 协作提交全流程指南
  • 濮阳做网站星月网络建设通网站会员免费吗
  • 公司网站域名的设计企业形象网站策划方案
  • 工信部公布网站备案拍照背景电子商务 做网站
  • 动态知识蒸馏(Dynamic KD)技术详解
  • 基于单片机的超声波自动泥浆回收系统
  • 了解和使用多态
  • 企业网站开发的文献综述网站开发的中期工作
  • 广州市门户网站建设宝应建设局网站
  • 做视频网站要什么格式好网站建设与管理指什么
  • 基于防伪标签的吊牌防伪:品牌核心防护环节
  • 国产的编程语言
  • 条款36:如果异步是必需的,请指定为std::launch::async
  • 建网站广州中国建行官网登录首页
  • 连云港网站建设方案西安网站建设多少钱
  • 网络管理(NM)
  • 【第1章>第4节】基于FPGA的图像腐蚀处理算法的测试以及MATLAB辅助验证
  • 脉冲在克尔效应下的频谱展宽仿真:原理与 MATLAB 实现
  • PPP工作法:贝索斯做事的方法