《Effective Modern C++》学习笔记 - Item 38: 注意future析构函数的不同行为

  • std::thread 和非延迟的 std::future 内部都对应着一个实际的线程,可以认为这些对象是我们对系统线程的 handle

  • 从这个角度来看,std::thread 和 future 在析构函数中有不同的行为是比较奇怪的:如 Item 37 所述,销毁一个 joinable 的 std::thread 会导致程序终止;然而销毁 future 有时会表现地如隐式执行了 join,有时如隐式执行了 detach,有时二者皆非,但无论如何都不会导致程序终止。本节中,我们就对 future 的行为进行更仔细的观察。

  • 首先,future 位于调用者(caller)和被调用者(callee)通信信道中调用者一侧。被调用者将其计算结果(一般通过一个 std::promise 对象)写入信道,而调用者通过 future 的 get() 函数读取该结果,示意图如下:
    在这里插入图片描述

  • 这里就有一个问题:被调用者的结果是存储在哪里的呢?

    • 不能被存储在被调用者创建的 std::promise 对象中。因为被调用者可能在调用者通过 get() 获取结果前就已经结束执行,其创建的所有对象已被销毁。
    • 也不能存储在调用者的 future 中,因为 std::future 可能被用来创建 std::shared_future 并将被调用者的结果转交给后者。std::shared_future 可以被拷贝,然而不是所有结果类型都可以(例如 move-only 的类型),所以它只能被放在其中一个 future 对象中。然而多个 future 对象怎么知道自己是否是应该持有结果的那个呢?
  • 结果实际是被存储在一个二者之外的地方,称为 shared state,通常是一个在堆上分配的对象,但是具体实现C++标准没有规定。调用者,被调用者和 shared state 间的数据流向如下图所示:
    在这里插入图片描述

  • future 对象的析构函数的行为就由与其关联的 shared state 决定:

    • 引用同一个 通过 std::async 创建非延迟 的 shared state 的 最后一个 future 对象的析构函数,会 阻塞 直至执行完成,相当于对线程进行了隐式的 join 。
    • 其它所有 future 对象的析构函数只是销毁自身,类似于对线程隐式执行了 detach。对于最后一个持有某延迟的任务的 future 对象,这也意味着延迟的任务永远不会被执行。
  • 可以看出,相比 std::thread,future 采取了更“温和”的措施:隐式的 join,而非终止程序。标准委员会对此也有争议,不过这就是C++11/14最终的决定。

  • 如果你能从程序逻辑上判断一个 future 对象满足以上三个条件中的任意一个,那么其析构函数就不会阻塞程序;否则就有可能阻塞(而且无法通过其它方式判断)。

  • 如果需要确保 future 不会阻塞,那么可以通过打破 “通过 std::async 创建” 这个条件实现,具体来说可以使用 std::packaged_task。该对象可以认为比 std::async 低一层级,其作用是将一个函数包装成结果会被放入一个 shared state 的样子,以供异步执行。它的 future 对象可以通过 get_future() 获得。创建后,它可以被放在一个 std::thread 上运行:

{ 									// begin block
	std::packaged_task<int()>
	pt(calcValue);
	auto fut = pt.get_future();
	std::thread t(std::move(pt));	// std::packaged_task can't be copied
	... 							// see below
}		 							// end block

如此,程序的行为规则将遵从 std::thread 的规则,在 “…” 的部分中:如果执行了 join 或 detach,则 future 在析构函数中无需再次如此做;否则线程 t 将会在作用域结束时仍然 joinable,导致程序终止。future 的析构函数不会再有阻塞的可能。

总结

  1. future 的析构函数大部分情况下只是销毁其数据成员。
  2. 引用同一个通过 std::async 创建的非延迟的 shared state 的最后一个future 对象的析构函数会阻塞直至任务执行完成。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值