命名空间
变体
操作

std::async

来自 cppreference.com
< cpp‎ | thread
 
 
并发支持库
线程
(C++11)
(C++20)
this_thread 命名空间
(C++11)
(C++11)
(C++11)
协作取消
互斥
(C++11)
通用锁管理
(C++11)
(C++11)
(C++11)
(C++11)
(C++11)
条件变量
(C++11)
信号量
闩锁和屏障
(C++20)
(C++20)
期货
(C++11)
(C++11)
async
(C++11)
(C++11)
安全回收
(C++26)
危险指针
原子类型
(C++11)
(C++20)
原子类型的初始化
(C++11)(C++20 中已弃用)
(C++11)(C++20 中已弃用)
内存排序
原子操作的自由函数
原子标志的自由函数
 
定义在头文件 <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,该期货最终将保存该函数调用的结果。

1) 行为方式与使用 policystd::launch::async | std::launch::deferred 调用 (2) 相同。
2) 根据特定的启动策略 policy(见下文)调用带有参数 args 的函数 f

std::async 的返回类型为 std::future<V>,其中 V

typename std::result_of<typename std::decay<F>::type(
                        typename std::decay<Args>::type...)>::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)),
       decay-copy(std::forward<Args>(args))...)

(直到 C++23)

std::invoke(auto(std::forward<F>(f)),
            auto(std::forward<Args>(args))...)

(从 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::asyncstd::launch::deferred,或任何实现定义的策略标志都没有设置在 policy 中,则行为未定义。

[edit] 策略选择

如果设置了多个标志,则选择哪个策略由实现定义。对于默认情况(std::launch::asyncstd::launch::deferred 标志都设置在 policy 中),标准建议(但不强制要求)使用可用并发性,并推迟任何其他任务。

如果选择了 std::launch::async 策略,

  • 对与共享此 std::async 调用创建的共享状态的异步返回对象的等待函数的调用会阻塞,直到关联的线程完成,就像被连接一样,或者超时;并且
  • 关联的线程完成从第一个等待共享状态的函数成功返回,或者与最后一个释放共享状态的函数的返回,两者中先发生的同步。

[edit] 异常

抛出

[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]
  1. 可移动构造性已由 std::is_constructible_v 间接要求。

[编辑] 另请参阅

(C++11)
等待异步设置的值
(类模板) [编辑]