C++ 多线程编程(三) 获取线程的返回值——future

C++11引入的<future>库提供了获取线程返回值的方法,包括future、promise、packaged_task和async四个关键类。future作为存储线程结果的媒介,不能直接获取线程返回值;promise允许原子地修改future的状态;packaged_task结合了任务和future,可直接执行任务并返回结果;async则是一个综合函数,支持同步或异步执行任务。所有这些工具都涉及到线程间的共享状态管理和结果获取。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

C++11标准库增加了获取线程返回值的方法,头文件为<future>,主要包括futurepromisepackaged_taskasync四个类。

那么,了解一下各个类的构成以及功能。

1 future

future是一个模板类,它是传输线程返回值(也称为共享状态)的媒介,也可以理解为线程返回的结果就安置在future中。

future版本:C++11      头文件<future>
构造函数

1. future() noexcept;

默认构函数,是一个无效future,valid函数返回false.

2. future(future&& other) noexcept

移动构造函数,使用后other.valid()为false。

3. future(const future& other) = delete

禁用拷贝构造函数。

析构函数~future();
get

std::future<T>  T get();

获取共享状态,如果共享状态尚未就绪(比如线程未结束),则堵塞,等效于wait函数。

valid

bool valid() const noexcept;

判断future是否有效。无效future有三种情况:一是使用默认构造函数创建的;调用过移动构造函数;三是调用过get函数

wait

void wait() const;

获取共享状态,如果共享状态尚未就绪(比如线程未结束),则堵塞;阻塞结束之后,还需要继续调用get才能获取共享状态,此时get不再阻塞。

wait_for设置最大阻塞时间
wait_until设置最大阻塞时刻

跟多线程相关的类,大多都有一个特点,就是禁用拷贝构造函数,仅能使用移动构造函数。

单独使用future依然没有办法获取线程的返回值,必须与promise、packaged_task或者async搭配使用。而且,单独创建出来的future对象甚至都没有意义,必须从promise、packaged_task或者async中创建,才有意义。

2 promise

promise对象可以看做是future类的封装,它可以原子的修改future的共享状态。

promiseC++ 11                  头文件: <future>
构造函数

1. promise();

默认构造函数,构造一个共享状态为空的 std::promise

2. promise(promise&& other) noexcept;

移动构造函数,用原属 other 的共享状态构造新的 std::promise 对象,使用移动语义。构造完毕后, other 无共享状态;

3. future(const future& other) = delete;

禁用拷贝构造函数。

get_future

std::future<T> get_future();

获取共享状态,返回一个future对象。

set_value原子地存储 value 到共享状态,并令状态就绪(future::get结束阻塞)。
set_value_at_thread_exit原子地存储 value 到共享状态,而不立即令状态就绪。在当前线程退出时,再令状态就绪。
#include <thread>
#include <future>
#include <iostream>

void fun1(std::promise<int> pro) {
    std::this_thread::sleep_for(std::chrono::seconds(3));
    pro.set_value(100);
    return;
};

int main()
{
    std::promise<int> pro;
    std::future<int> fut = pro.get_future();
    std::thread th(&fun1, std::move(pro));
    th.detach();
    std::cout << fut.get() << std::endl;
}

需要注意的一点是,future的模板类型与线程函数的返回类型无关。上边fun1函数的返回值为void,但是future类型为int。

3 packaged_task

packaged_task是一个类,它主要包含了一个future对象和一个任务,这个任务可以是函数、 lambda 表达式、 bind 表达式或其他函数对象。

packaged_taskC++11                      头文件: <future>
构造函数

template<class F>

explicit packaged_task(F&& f);

f 是一个函数,也就是任务入口。

get_future

std::future<T> get_future();

获取共享状态,返回一个future对象。

()运算符

void operator()(ArgTypes...args);

args 为参数运行存储的任务;任务结束时,将返回值存储于共享状态,并令共享状态就绪。该函数会新开一个线程并以detach模式运行新线程。

#include <iostream>
#include <cmath>
#include <thread>
#include <future>
#include <functional>
 
// 避免对 std::pow 重载集消歧义的独有函数
int f(int x, int y) { return std::pow(x,y); }
 
void task_lambda()
{
    std::packaged_task<int(int,int)> task([](int a, int b) {
        return std::pow(a, b); 
    });
    std::future<int> result = task.get_future();
 
    task(2, 9);
 
    std::cout << "task_lambda:\t" << result.get() << '\n';
}
 
void task_bind()
{
    std::packaged_task<int()> task(std::bind(f, 2, 11));
    std::future<int> result = task.get_future();
 
    task();
 
    std::cout << "task_bind:\t" << result.get() << '\n';
}
 
void task_thread()
{
    std::packaged_task<int(int,int)> task(f);
    std::future<int> result = task.get_future();
 
    std::thread task_td(std::move(task), 2, 10);
    task_td.join();
 
    std::cout << "task_thread:\t" << result.get() << '\n';
}
 
int main()
{
    task_lambda();
    task_bind();
    task_thread();
}

输出:

 与promise不同的是,packaged_task任务的返回类型就是future的类型。

4 async

async是一个模板函数,它综合了前边promise和packaged_task的功能,一个函数就可以实现线程创建、任务执行、获取返回值等功能。

async的返回值是一个future对象,async所执行的任务完成后,会令共享状态进入就绪状态。

#include <thread>
#include <future>
#include <iostream>
#include <unistd.h>

int main()
{
    auto ff = std::async(std::launch::async, []{ sleep(2); return 2.3; }); 

    std::cout << ff.get() << std::endl;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值