假设函数 f 要传递给 std::async 以执行,则:
std::launch::async启动策略意味着函数必须以异步方式运行,即在另一线程上执行。std::launch::deferred启动策略意味着函数只会在std::async所返回的期值的get或wait得到调用时才执行。即执行会推迟至其中一个调用发生的时刻。当调用get或wait时,f会同步运行。即调用方会阻塞至f运行结束为止。如果get和wait没有得到调用,f是不会运行的。
std::async 的默认启动策略,是对上面二者或运算的结果。下面两个调用有着完全相同的意义:
auto fut1 = std::async(f);
auto fut2 = std::async(std::launch::async |
std::launch::deferred,
f);这么一来,默认启动策略就允许 f 以异步或同步的方式运行皆可。这种弹性使得 std::async 与标准库的线程管理组件能够承担得起线程的创建和销毁、避免超定,以及负载均衡的责任。
但默认启动策略会带来一些问题,若给定线程 t 执行:
auto fut - std::async(f);则:
- 无法预知
f是否会和t并发运行。 - 无法预知
f是否运行在与调用fut的get或wait函数的线程不同的某线程之上。 - 连
f是否会运行都是无法预知的。
默认启动策略在调度上的弹性常会在使用 thread_local 变量时导致混淆。这意味着如果 f 读写此线程级局部存储(TLS)时,无法预知会取到的是哪个线程的局部存储。这也会影响哪些基于 wait 的循环中以超时为条件的。因为对任务调用 wait_for 或者 wait_until 会产生 std::launch::deferred 一值。
下面的循环貌似最终会停止,但实际上可能会永远运行下去:
using namespace std::literals;
void f()
{
std::this_thread::sleep_for(1s);
}
auto fut = std::async(f);
while (fut.wait_for(100ms) !=
std::future_status::ready)
{
...
}如果 f 与调用 std::async 的线程是并发执行的,这就没问题。如果 f 被推迟执行,则 fut.wait_for 终会返回 std::future_status::deferred,循环永远不会停止。
修正这个缺点并不难:
auto fut = std::async(f);
if (fut.wait_for(0s) ==
std::future_status::deferred)
{
// 使用fut的wait或get以异步方式调用f
} else {
while (fut.wait_for(100ms) !=
std::future_status::ready)
{
// 任务被推迟,且未就绪
// 则做并发工作,直至任务就绪
}
}
// fut就绪以默认启动策略对任务使用 std::async 能正常工作需要满足以下所有条件:
- 任务不需要与调用
get或wait的线程并发执行。 - 读写哪个下次的
thread_local变量并无影响。 - 或者可以给出保证在
std::async返回的期值上调用get或wait,或者可以接收任务可能永不执行。 - 使用
wait_for或wait_until的代码会将任务被推迟的可能性纳入考量。
其中一个条件不满足,你就可能想要确保任务以异步执行。实现这一点的手法,就是指定 std::launch::async :
auto fut = std::async(std::launch::async, f);可以撰写一个函数,保证任务以异步方式执行,这是C++11实现版本:
template<typename F, typename... Ts>
inline
std::future<typename std::result_of<F(Ts...)>::type>
reallyAsync(F&& f, Ts&&... params)
{
return std::async(std::launch::async,
std::forward<F>(f),
std::forward<Ts>(params)...);
}C++14可以简化实现:
template<typename F, typename... Ts>
inline auto
reallyAsync(F&& f, Ts&&... params)
{
return std::async(std::launch::async,
std::forward<F>(f),
std::forward<Ts>(params)...);
}reallyAsync 的用法就像 std::async:
auto fut = reallyAsync(f); // 以异步方式运行f