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
的完成
按顺序先于
共享状态就绪。
目录 |
参数
| f | - | 要调用的 可调用 对象 |
| 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 起) |
惰性求值 被执行:
-
对由
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(); }); // 临时对象的析构函数等待 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 << "The sum is " << parallel_sum(v.begin(), v.end()) << '\n'; X x; // 以默认策略调用 (&x)->foo(42, "Hello"): // 可能并发打印 "Hello 42" 或延迟执行 auto a1 = std::async(&X::foo, &x, 42, "Hello"); // 以延迟策略调用 x.bar("world!") // 在调用 a2.get() 或 a2.wait() 时打印 "world!" auto a2 = std::async(std::launch::deferred, &X::bar, x, "world!"); // 以异步策略调用 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"
可能的输出:
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 时 可能抛出 |
| 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++ 文档
中关于
执行支持库
的内容
|
|