std::async
在标头 <future> 定义
|
||
(1) | ||
template< class Function, class... Args > std::future<typename std::result_of<typename std::decay<Function>::type( |
(C++11 起) (C++17 前) |
|
template< class Function, class... Args > std::future<std::invoke_result_t<std::decay_t<Function>, |
(C++17 起) (C++20 前) |
|
template< class Function, class... Args > [[nodiscard]] std::future<std::invoke_result_t<std::decay_t<Function>, |
(C++20 起) | |
(2) | ||
template< class Function, class... Args > std::future<typename std::result_of<typename std::decay<Function>::type( |
(C++11 起) (C++17 前) |
|
template< class Function, class... Args > std::future<std::invoke_result_t<std::decay_t<Function>, |
(C++17 起) (C++20 前) |
|
template< class Function, class... Args > [[nodiscard]] std::future<std::invoke_result_t<std::decay_t<Function>, |
(C++20 起) | |
函数模板 std::async
异步地运行函数 f(有可能在可能是线程池一部分的分离线程中),并返回最终将保有该函数调用结果的 std::future。
任何情况下,对 std::async
的调用同步于(定义见 std::memory_order)对 f 的调用,且 f 的完成先序于令共享状态就绪。
如果 INVOKE(decay-copy(std::forward<F>(f)), |
(C++20 前) |
在满足以下任一条件时,程序非良构:
|
(C++20 起) |
参数
f | - | 要调用的可调用 (Callable) 对象 | ||||||
args... | - | 传递给 f 的参数 | ||||||
policy | - | 位掩码值,每个单独位控制允许的执行方法
|
返回值
指代此次调用 std::async
所创建的共享状态的 std::future。
启动策略
异步调用
如果有设置异步 标志(即 (policy & std::launch::async) != 0),那么
|
(C++23 前) |
|
(C++23 起) |
decay-copy 的调用在当前线程求值 (C++23 前)auto 产生的值在当前线程实质化 (C++23 起)。如果 f 返回值或抛出异常,那么它们会存储到 std::async
返回给调用方的 std::future 的共享状态中。
推迟调用
如果有设置推迟 标志(即 (policy & std::launch::deferred) != 0),那么 std::async
会将 decay-copy(std::forward<F>(f)) 和 decay-copy(std::forward<Args>(args))... (C++23 前)auto(std::forward<F>(f)) 和 auto(std::forward<Args>(args))... (C++23 起) 存储到共享状态中。
进行惰性求值:
- 在
std::async
返回给调用方的 std::future 上首次调用非定时等待函数会在当前线程(不一定是最初调用std::async
的线程)中求值 INVOKE(std::move(g), std::move(xyz)),其中
- g 是存储的 decay-copy(std::forward<F>(f)) (C++23 前)auto(std::forward<F>(f)) (C++23 起) 的值,并且
- xyz 是存储的 decay-copy(std::forward<Args>(args))... (C++23 前)auto(std::forward<Args>(args))... (C++23 起) 的副本。
- 将结果或异常置于关联到该 std::future 的共享状态,然后才令它就绪。对同一 std::future 的所有后续访问都会立即返回结果。
其他策略
如果 policy 中没有设置 std::launch::async,std::launch::deferred 或任何由实现定义的其他策略标志,那么行为未定义。
策略选择
如果 policy 中设置了多个标志,那么由实现定义选择哪个策略。对于默认情况(policy 中同时设置了 std::launch::async 和 std::launch::deferred 两个标志),标准建议(但不要求)利用现有的并发资源,并且推迟另外的任务。
如果选择了 std::launch::async 策略,那么:
- 对于共享该
std::async
创建的共享状态的某个异步返回对象,对它的等待函数的调用会阻塞,直到关联的线程如同以结束执行或超时的方式完成。 - 关联线程的完成同步于 首个在共享状态上等待的函数的成功返回和最后一个释放共享状态的函数的返回,两者中的先到来的一方。
异常
可能会抛出以下异常:
- 如果无法分配内部数据结构所用的内存,那么就会抛出 std::bad_alloc。
- 如果 policy == std::launch::async 且实现无法开始新线程,那么就会抛出以 std::errc::resource_unavailable_try_again 为错误条件的 std::system_error。
- 此时如果 policy 是 std::launch::async | std::launch::deferred 或者设置了额外位,那么就会回退到推迟调用或其他由实现定义的策略。
注解
实现可以通过在默认运行策略中启用额外(实现定义的)位,扩展 std::async
重载 (1) 的行为。
由实现定义的启动策略的例子是同步策略(在 std::async
调用内立即执行)和任务策略(类似 std::async
,但不清理线程局域对象)。
如果从 std::async
获得的 std::future 没有被移动或绑定到引用,那么在完整表达式结尾, std::future 的析构函数将阻塞到异步计算完成,实质上令如下代码同步:
std::async(std::launch::async, []{ f(); }); // 临时量的析构函数等待 f() std::async(std::launch::async, []{ g(); }); // f() 完成前不开始
注意:以调用 std::async
以外的方式获得的 std::future 的析构函数不会阻塞。
示例
#include <algorithm> #include <future> #include <iostream> #include <mutex> #include <numeric> #include <string> #include <vector> std::mutex m; struct X { void foo(int i, const std::string& str) { std::lock_guard<std::mutex> lk(m); std::cout << str << ' ' << i << '\n'; } void bar(const std::string& str) { std::lock_guard<std::mutex> lk(m); std::cout << str << '\n'; } int operator()(int i) { std::lock_guard<std::mutex> lk(m); std::cout << i << '\n'; return i + 10; } }; template<typename RandomIt> int parallel_sum(RandomIt beg, RandomIt end) { auto len = end - beg; if (len < 1000) return std::accumulate(beg, end, 0); RandomIt mid = beg + len / 2; auto handle = std::async(std::launch::async, parallel_sum<RandomIt>, mid, end); int sum = parallel_sum(beg, mid); return sum + handle.get(); } int main() { std::vector<int> v(10000, 1); std::cout << "和为 " << parallel_sum(v.begin(), v.end()) << '\n'; X x; // 以默认策略调用 x.foo(42, "Hello") : // 可能同时打印 "Hello 42" 或延迟执行 auto a1 = std::async(&X::foo, &x, 42, "Hello"); // 以 deferred 策略调用 x.bar("world!") // 调用 a2.get() 或 a2.wait() 时打印 "world!" auto a2 = std::async(std::launch::deferred, &X::bar, x, "world!"); // 以 async 策略调用 X()(43) : // 同时打印 "43" auto a3 = std::async(std::launch::async, X(), 43); a2.wait(); // 打印 "world!" std::cout << a3.get() << '\n'; // 打印 "53" } // 若 a1 在此点未完成,则 a1 的析构函数在此打印 "Hello 42"
可能的输出:
和为 10000 43 world! 53 Hello 42
缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告 | 应用于 | 出版时的行为 | 正确行为 |
---|---|---|---|
LWG 2021 | C++11 | 返回类型不正确, deferred 的情况下参数的值类别不明 | 更正返回类型,明确使用右值 |
LWG 2078 | C++11 | 不明确 policy 在除 std::launch::async 外还指定了 其他启动策略时是否会抛出 std::system_error |
仅限 policy == std::launch::async |
LWG 2100 | C++11 | 使用 std::launch::async 策略时计时等待函数不能超时 | 可以超时 |
LWG 2120 | C++11 | 未设置标准或实现定义的策略的情况下行为不明 | 行为未定义 |
LWG 2752 | C++11 | std::async 在无法分配内部数据结构所用的内存时不一定会抛出 std::bad_alloc |
会抛出 |
LWG 3476 | C++11 | 要求 Function 与 Args... 可移动构造 (MoveConstructible) ,但没有指定的额外移动构造 |
移除要求 |