多线程编程6. std::future、std::promise、std::packaged_task和std::async

1. std::future

提供了一种访问异步操作结果的机制。
如果我们想要得到一个线程运行的结果,一般来说,需要用到全局变量,但是从安全的角度上来说,全局变量并不合适。因此,C++11提供了std::future类模板。

获取future结果有三种方式:
get():等待异步操作(线程)结束并返回结果
wait():只是等待异步操作完成,没有返回值
wait_for():根据一个条件等待返回结果。

wait_for 有三种状态:
deferred:异步操作还没开始
ready:异步操作已经完成
timeout:异步操作超时

std::future_status status;
do {
    status = future.wait_for(std::chrono::seconds(1));
    if (status == std::future_status::deferred) {
        std::cout << "尚未开始\n";
    } else if (status == std::future_status::timeout) {
        std::cout << "超时\n";
    } else if (status == std::future_status::ready) {
        std::cout << "已经完成\n";
    }
} while (status != std::future_status::ready); 

2. std::promise

std::promise 包裹的是一个值,线程通过 std::promise 来实现这个包裹值的传递。
使用场景:为获取线程函数中的某个值,可以在线程函数中给外面传进来的promise赋值,当线程函数执行完成之后,外部就可以通过promise获取该值了
注意:需要通过std::future 来获取 std::promise中包裹的值。

std::promise<int> value;
std::thread t([](std::promise<int>& input){
    input.set_value_at_thread_exit(9);
}, std::ref(value));  // 需要用 std::ref 表示传参是引用

std::future<int> result = value.get_future();
auto res = result.get();

3. std::packaged_task

与 std::promise 有点类似,但是 std::packaged_task 包裹的是一个可调用对象,它包裹的这个可调用对象可以作为线程入口函数。
用它包裹的好处是,可以获取线程函数运行之后的返回结果。

std::packaged_task<int()> task([](){
    return 7;
});
std::thread t1(std::ref(task));   // 注意要用 std::ref
std::future<int> f1 = task.get_future();   // 获取线程函数运行后的结果
auto r1 = f1.get();

4. std::future、std::promise 和 std::packaged_task 三者之间的关系

std::promise 包装的是一个值,通过 std::future 来获取包装值的结果
std::packaged_task 包装的是一个函数,通过 std::future 来获取包装函数执行完的结果
两者都通过 std::future 来获取结果

5. std::async

5.1 介绍

如果不用std::async,我们想获取线程函数的返回值,需要定义一个变量,在线程函数中去给这个变量赋值,然后join, 最后得到结果。
当然也可以用前面提到的 std::packaged_task 包装线程函数,再通过std::future获取值。

std::async会自动创建一个线程去调用线程函数,它返回一个std::future,这个future中存储了线程函数返回的结果。

int get_value() {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    return 42;
}

std::future<int> future_result = std::async(std::launch::async, get_value );
int result = future_result.get(); // 获取异步操作的结果
std::cout << result << std::endl; // 输出42

参数介绍:
第一个参数:表示何时创建线程的标志
std::launch::deferred:延迟到调用 get() 或者 wait() 时执行,如果不调用就不会执行
std::launch::async:立即创建出新线程来运行入口函数
std::launch::async | std::launch::deferred:系统自行决定异步还是同步运行,不传参也是该效果

第二个参数:传入一个可调用对象,作为线程的入口函数

5.2 std::async 和 std::thread 区别

  1. async()可以通过future 获取到返回值。
  2. async()不一定创建新线程,系统自行判断,thread()一定创建。
    std::launch::deferred,一般就不会报异常,如果系统资源紧张,无法创建新线程,async不加额外参数的调用方式就不会创建新线程。而是后续哪个线程调用get()请求结果,就执行在这个调用get()的线程上。
    std::launch::async ,立马创建新的线程。承受的代价是,系统资源紧张时可能崩溃。
  • 8
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值