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::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 生成的值在当前线程中物化 (materialized)。 |
(自 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 的首次非定时等待函数的调用,将在调用等待函数的线程(不必是最初调用std::async
的线程)中求值 INVOKE(std::move(g), std::move(xyz)),其中
|
(直到 C++23) |
|
(自 C++23 起) |
- 结果或异常放置在与返回的 std::future 关联的共享状态中,然后才使其就绪。所有后续对同一 std::future 的访问都将立即返回结果。
[edit] 其他策略
如果在 policy 中既未设置 std::launch::async,也未设置 std::launch::deferred,也未设置任何实现定义的策略标志,则行为是未定义的。
[edit] 策略选择
如果设置了多个标志,则选择哪个策略是实现定义的。对于默认情况(在 policy 中同时设置了 std::launch::async 和 std::launch::deferred 标志),标准建议(但不要求)利用可用的并发性,并延迟任何额外的任务。
如果选择了 std::launch::async 策略,则
- 在异步返回对象上的等待函数的调用(该对象共享由此
std::async
调用创建的共享状态)将阻塞,直到关联的线程完成(如同已连接),或者超时;并且 - 关联的线程完成与第一个等待共享状态的函数成功返回,或与释放共享状态的最后一个函数返回同步,以先发生者为准。
[edit] 异常
抛出
- 如果无法分配内部数据结构的内存,则抛出 std::bad_alloc,或者
- 如果 policy == std::launch::async 且实现无法启动新线程,则抛出带有错误条件 std::errc::resource_unavailable_try_again 的 std::system_error。
- 如果 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
[edit] 缺陷报告
以下行为变更的缺陷报告被追溯应用于先前发布的 C++ 标准。
DR | 应用于 | 已发布行为 | 正确行为 |
---|---|---|---|
LWG 2021 | C++11 | 返回类型不正确且值类别 在延迟情况下参数不明确 |
更正了返回类型并 阐明了使用右值 |
LWG 2078 | C++11 | 不清楚是否 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 |
如果内部数据结构的内存无法分配,则 std::async 可能不会抛出 std::bad_alloc
|
C++11 | 抛出 LWG 3476 |
C++20 |
F 的(衰变类型)和参数类型
|
直接要求是可移动构造的 | 移除了这些要求[1] ↑ 移动构造性已经由 std::is_constructible_v 间接要求。 |
[edit] 参见 |
- 等待异步设置的值
(类模板)
(C++11) |
C++ 文档 关于 执行支持库 取自 "https://cppreference.cn/mwiki/index.php?title=cpp/thread/async&oldid=177317" |
导航
|