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, F&& f, Args&&... args ); |
(2) | (自 C++11 起) |
函数模板 std::async
异步运行函数 f(可能在单独的线程中,该线程可能是线程池的一部分)并返回一个 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 的完成 按顺序发生在 使共享状态就绪之前。
内容 |
[edit] 参数
f | - | Callable 对象以供调用 |
args | - | 传递给 f 的参数 |
policy | - | 位掩码值,其中各个位控制允许的执行方法 |
[edit] 返回值
std::future,它引用此 std::async
调用创建的共享状态。
[edit] 启动策略
[edit] 异步调用
如果设置了 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 可访问的共享状态中。
[edit] 延迟调用
如果设置了 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 开始) |
延迟评估是在
- 第一次对
std::async
返回给调用者的 std::future 上的非计时等待函数的调用将评估 INVOKE(std::move(g), std::move(xyz)) 在调用等待函数的线程中(不必是最初调用std::async
的线程),其中
|
(直到 C++23) |
|
(从 C++23 开始) |
- 结果或异常被放置在与返回的 std::future 关联的共享状态中,并且仅在此之后才将其设为就绪。对同一个 std::future 的所有后续访问都将立即返回结果。
[edit] 其他策略
如果 std::launch::async 或 std::launch::deferred,或任何实现定义的策略标志都没有设置在 policy 中,则行为未定义。
[edit] 策略选择
如果设置了多个标志,则选择哪个策略由实现定义。对于默认情况(std::launch::async 和 std::launch::deferred 标志都设置在 policy 中),标准建议(但不强制要求)使用可用并发性,并推迟任何其他任务。
如果选择了 std::launch::async 策略,
- 对与共享此
std::async
调用创建的共享状态的异步返回对象的等待函数的调用会阻塞,直到关联的线程完成,就像被连接一样,或者超时;并且 - 关联的线程完成与从第一个等待共享状态的函数成功返回,或者与最后一个释放共享状态的函数的返回,两者中先发生的同步。
[edit] 异常
抛出
- std::bad_alloc,如果无法分配内部数据结构的内存,或者
- std::system_error,其中错误条件为 std::errc::resource_unavailable_try_again,如果 policy == std::launch::async 并且实现无法启动新线程。
- 如果 policy 为 std::launch::async | std::launch::deferred 或设置了其他位,它将在这种情况下回退到延迟调用或实现定义的策略。
[edit] 备注
实现可以通过在默认启动策略中启用其他(实现定义的)位来扩展 std::async
的第一个重载的行为。
实现定义的启动策略的示例包括 sync 策略(立即执行,在 std::async
调用中)和 task 策略(类似于 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 的析构函数永远不会阻塞。
[edit] 示例
#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++ 标准。
DR | 应用于 | 已发布的行为 | 正确的行为 |
---|---|---|---|
LWG 2021 | C++11 | 返回值类型不正确,且参数的 值类别在延迟情况下不清楚 |
更正了返回值类型,并 明确指出使用了右值 |
LWG 2078 | C++11 | 尚不清楚是否 std::system_error 可能会在 policy 指定除 std::launch::async 之外的其他 |
启动策略时抛出 仅当策略 == 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++20 | (F 的衰减类型和) 参数类型直接要求可移动构造 |
删除了这些要求[1] |
- ↑ 可移动构造性已由 std::is_constructible_v 间接要求。
[编辑] 另请参阅
(C++11) |
等待异步设置的值 (类模板) |