当前位置:网站首页>Item 36: Specify std::launch::async if asynchronicity is essential.
Item 36: Specify std::launch::async if asynchronicity is essential.
2022-08-01 00:23:00 【loongknown】
Item 36: Specify std::launch::async if asynchronicity is essential.
当你使用 std::async() 执行一个函数或可调用对象时,你通常期望这个函数是异步执行。但是, std::async() 不一定如你所愿。其实 std::async() 是根据执行策略决定是否会异步执行。 std::async() 有两种执行策略,定义在 std::launch 作用域中:
std::launch::async函数或可执行对象必须异步执行,也即运行在其他线程上。std::launch::deferred函数或可执行对象延迟执行。仅在std::async()的返回对象std::future调用get或wait时,才在当前线程同步执行,并且调用者会阻塞直到函数执行完成。
std::async() 的默认策略其实是二者的组合,也即以下两者涵义完全相同:
auto fut1 = std::async(f); // run f using default launch policy
auto fut2 = std::async(std::launch::async | // run f either
std::launch::deferred, // async or
f); // deferred
默认的策略下,f 可能是同步执行也可能是异步执行。正如 Item 35: Prefer task-based programming to thread-based. 中讨论的:标准库的线程管理模块承担了线程的创建和释放的职责,可以有效避免超额订阅、保证负载均衡。这极大地方便了 std::async 的使用。
但是,默认策略也会有如下问题:
- 无法预测
f是否是并发执行。 - 无法预测
f是否运行在get或wait调用时的线程上。 - 甚至无法预测
f是否已经执行了。因为没法保证一定会调用get或wait。
当 f 要访问本地线程存储(TLS,Thread Local Storage)时,无法预测访问的是哪个线程的本地存储。
auto fut = std::async(f); // TLS for f possibly for
// independent thread, but
// possibly for thread
// invoking get or wait on fut
std::async 的默认策略还会影响到 wait_for 超时调用写法,可能导致 bug,例如:
using namespace std::literals; // for C++14 duration suffixes; see Item 34
void f() // f sleeps for 1 second, then returns
{
std::this_thread::sleep_for(1s);
}
auto fut = std::async(f); // run f asynchronously (conceptually)
while (fut.wait_for(100ms) != // loop until f has
std::future_status::ready) // finished running...
{
// which may never happen!
…
}
如果 std::async 是并发执行,也即执行策略为 std::launch::async,以上代码没有问题。但是,如果执行策略为 std::launch::deferred时,fut.wait_for 总是返回 future_status::deferred,以上代码就会有问题。解决办法也很简单,先通过 wait_for 的超时时间为 0 来检测 std::async 是异步执行还是同步执行:
auto fut = std::async(f); // as above
if (fut.wait_for(0s) == // if task is
std::future_status::deferred) // deferred...
{
// ...use wait or get on fut
… // to call f synchronously
} else {
// task isn't deferred
while (fut.wait_for(100ms) != // infinite loop not
std::future_status::ready) {
// possible (assuming
// f finishes)
… // task is neither deferred nor ready,
// so do concurrent work until it's ready
}
… // fut is ready
}
综上,如果你的使用场景不是以下几种,则需要考虑是否需要替换 std::async 的默认策略:
- 当调用
get或wait时,任务不需要并发执行。 - 并不关心访问的是哪个线程的本地存储。
- 可以保证
get或wait一定会被调用,或者任务不被执行也能接受。 - 使用
wait_for或wait_until时,需要考虑std::launch::deferred策略。
如果不是以上场景,你可能需要指定使用 std::launch::async 策略,也即真正创建一个线程去并发执行任务:
auto fut = std::async(std::launch::async, f); // launch f asynchronously
这里提供一个并发执行任务的封装:
template<typename F, typename... Ts> // C++11
inline
std::future<typename std::result_of<F(Ts...)>::type>
reallyAsync(F&& f, Ts&&... params) // return future
{
// for asynchronous
return std::async(std::launch::async, // call to f(params...)
std::forward<F>(f),
std::forward<Ts>(params)...);
}
reallyAsync 接受一个可执行对象 f 和 多个参数 params,并完美转发给 std::async ,同时使用 std::launch::async 策略。C++14 版本如下:
template<typename F, typename... Ts>
inline
auto // C++14
reallyAsync(F&& f, Ts&&... params)
{
return std::async(std::launch::async,
std::forward<F>(f),
std::forward<Ts>(params)...);
}
至此,本文结束。
参考:
边栏推荐
猜你喜欢

NIO编程

新产品如何进行网络推广?

南方科技大学:Xiaoying Tang | AADG:视网膜图像分割领域泛化的自动增强

SVN服务器搭建+SVN客户端+TeamCity集成环境搭建+VS2019开发

C# Rectangle基本用法和图片切割

pycaret source code analysis: download dataset\Lib\site-packages\pycaret\datasets.py
![[Reading Notes -> Data Analysis] 02 Data Analysis Preparation](/img/e7/258daf851746cb043f301437ee3bbe.png)
[Reading Notes -> Data Analysis] 02 Data Analysis Preparation

pycaret源码分析:下载数据集\Lib\site-packages\pycaret\datasets.py

TFC CTF 2022 WEB Diamand WriteUp

Handwritten a simple web server (B/S architecture)
随机推荐
Named Entity Recognition - Model: BERT-MRC
【FPGA教程案例43】图像案例3——通过verilog实现图像sobel边缘提取,通过MATLAB进行辅助验证
信奥学习规划 信息学竞赛之路(2022.07.31)
leetcode:126. 单词接龙 II
2022年最新重庆建筑八大员(电气施工员)模拟题库及答案
Force buckle 2326, 197
[Reading Notes -> Data Analysis] 02 Data Analysis Preparation
Southern University of Science and Technology: Xiaoying Tang | AADG: Automatic Enhancement for Generalization in the Field of Retinal Image Segmentation
一文带你了解 Grafana 最新开源项目 Mimir 的前世今生
继承和友元,静态成员的关系
date命令
消息队列消息存储设计(架构实战营 模块八作业)
Carefully organize 16 MySQL usage specifications to reduce problems by 80% and recommend sharing with the team
Shell common scripts: Nexus batch upload local warehouse enhanced version script (strongly recommended)
逐步手撕轮播图3(保姆级教程)
Difference between first and take(1) operators in NgRx
编写方法将一个数组扁平化并且去重和递增排序
Flutter教程之 02 Flutter 桌面程序开发入门教程运行hello world (教程含源码)
cobaltstrike
Shell common script: Nexus batch upload local warehouse script