std::async
在头文件 <future> 中定义 |
||
template< class F, class... Args > std::future</* 见下文 */> async( F&& f, Args&&... args ); |
(1) | (C++11 起) |
template< class F, class... Args > std::future</* 见下文 */> async( std::launch policy, |
(2) | (C++11 起) |
函数模板 std::async
异步运行函数 f(可能在一个单独的线程中,该线程可能是线程池的一部分),并返回一个 std::future,该 std::future 最终将保存该函数调用的结果。
std::async
的返回类型是 std::future<V>,其中 V
是
typename std::result_of<typename std::decay<F>::type( |
(C++17 前) |
std::invoke_result_t<std::decay_t<F>, std::decay_t<Args>...>. |
(C++17 起) |
如果满足以下任何条件,程序将不正确:
|
(C++20 前) |
如果以下任一条件为 false,则程序格式错误
|
(C++20 起) |
对 std::async
的调用与对 f 的调用 同步,并且 f 的完成 先于 共享状态的就绪。
目录 |
[编辑] 参数
f | - | 要调用的 可调用(Callable)对象 |
args | - | 传递给 f 的参数 |
policy | - | 位掩码值,其中各个位控制允许的执行方法 |
[编辑] 返回值
std::future,引用此 std::async
调用创建的共享状态。
[编辑] 启动策略
[编辑] 异步调用
如果设置了 async 标志,即 (policy & std::launch::async) != 0,则 std::async
调用
INVOKE(decay-copy(std::forward<F>(f)), |
(直至 C++23) |
std::invoke(auto(std::forward<F>(f)), |
(C++23 起) |
如同在一个由 std::thread 对象表示的新执行线程中。
对 decay-copy 的调用在当前线程中求值。 |
(直至 C++23) |
由 auto 产生的值在当前线程中 实体化。 |
(C++23 起) |
如果函数 f 返回一个值或抛出异常,它将存储在通过 std::async
返回给调用者的 std::future 可访问的共享状态中。
[编辑] 延迟调用
如果设置了 deferred 标志(即 (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 起) |
执行延迟求值(Lazy evaluation)
- 对
std::async
返回给调用者的 std::future 的非定时等待函数的第一次调用将在调用等待函数的线程(不必是最初调用std::async
的线程)中评估 INVOKE(std::move(g), std::move(xyz)),其中
|
(直至 C++23) |
|
(C++23 起) |
- 结果或异常被放置在与返回的 std::future 关联的共享状态中,然后才使其就绪。所有后续对相同 std::future 的访问都将立即返回结果。
[编辑] 其他策略
如果在 policy 中既没有设置 std::launch::async 也没有设置 std::launch::deferred,也没有设置任何实现定义的策略标志,则行为未定义。
[编辑] 策略选择
如果设置了多个标志,则选择哪种策略由实现定义。对于默认情况(std::launch::async 和 std::launch::deferred 标志都在 policy 中设置),标准建议(但不要求)利用可用的并发性,并延迟任何额外的任务。
如果选择了 std::launch::async 策略,
- 对共享由
std::async
调用创建的共享状态的异步返回对象上的等待函数的调用将阻塞,直到关联线程完成(如同已加入),或者超时;并且 - 关联线程的完成与第一次等待共享状态的函数的成功返回,或与释放共享状态的最后一个函数的返回同步,以先发生者为准。
[编辑] 异常
抛出
- std::bad_alloc,如果无法分配内部数据结构的内存,或者
- std::system_error,错误条件为 std::errc::resource_unavailable_try_again,如果 policy == std::launch::async 且实现无法启动新线程。
- 如果 policy 是 std::launch::async | std::launch::deferred 或设置了额外的位,则在此情况下它将回退到延迟调用或实现定义的策略。
[编辑] 注意
实现可以通过在默认启动策略中启用额外的(实现定义的)位来扩展 std::async
的第一个重载的行为。
实现定义的启动策略示例包括同步策略(在 std::async
调用中立即执行)和任务策略(类似于 std::async
,但线程局部变量不清空)
如果从 std::async
获取的 std::future 未被移动或绑定到引用,则 std::future 的析构函数将在完整表达式的末尾阻塞,直到异步操作完成,从而使以下代码本质上同步
std::async(std::launch::async, []{ f(); }); // temporary's dtor waits for f() std::async(std::launch::async, []{ g(); }); // does not start until f() completes
请注意,通过调用 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 << "The sum is " << parallel_sum(v.begin(), v.end()) << '\n'; X x; // Calls (&x)->foo(42, "Hello") with default policy: // may print "Hello 42" concurrently or defer execution auto a1 = std::async(&X::foo, &x, 42, "Hello"); // Calls x.bar("world!") with deferred policy // prints "world!" when a2.get() or a2.wait() is called auto a2 = std::async(std::launch::deferred, &X::bar, x, "world!"); // Calls X()(43); with async policy // prints "43" concurrently auto a3 = std::async(std::launch::async, X(), 43); a2.wait(); // prints "world!" std::cout << a3.get() << '\n'; // prints "53" } // if a1 is not done at this point, destructor of a1 prints "Hello 42" here
可能的输出
The sum is 10000 43 world! 53 Hello 42
[编辑] 缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告 | 应用于 | 发布时的行为 | 正确的行为 |
---|---|---|---|
LWG 2021 | C++11 | 返回类型不正确且在延迟情况下参数的 值类别不明确 |
已更正返回类型并 澄清使用右值 |
LWG 2078 | C++11 | 不清楚如果 policy 指定了除了 std::launch::async 之外的其他启动策略, 是否会抛出 std::system_error 只有当 policy 为 std::launch::async 时才可能抛出 |
仅当 policy == std::launch::async 时才抛出 |
LWG 2100 | C++11 | 定时等待函数无法超时 如果使用 std::launch::async 策略 |
允许 |
LWG 2120 | C++11 | 如果未设置标准 或实现定义的策略,则行为不明确 |
在这种情况下,行为是 未定义的 |
LWG 2186 | C++11 | 不清楚延迟求值返回的值和抛出的 异常如何处理 |
它们存储在 共享状态中 |
LWG 2752 | C++11 | 如果无法分配内部数据结构的内存,std::async 可能不会抛出 std::bad_alloc如果无法分配内部数据结构的内存 |
抛出 |
LWG 3476 | C++20 | (衰变后的) F 类型和参数类型曾被直接要求为可移动构造的 |
已移除这些要求[1] |
- ↑ 可移动构造性已经通过 std::is_constructible_v 间接要求。
[编辑] 另见
(C++11) |
等待一个异步设置的值 (类模板) |
C++ 文档 for 执行支持库
|