【C++11多线程】获取异步任务的结果:future、shared_future

1.std::future

std::future 是个类模板,其源码如下所示。

在这里插入图片描述

std::future 可以用来获取异步任务的结果。std::future 通常由某个 Provider 创建,Provider 是一个异步任务的提供者,Provider 在某个线程中设置共享状态的值,与该共享状态相关联的 std::future 对象调用 get()(通常在另外一个线程中) 获取该值,如果共享状态的标志不为 ready,则调用 std::future::get() 会阻塞当前的调用者,直到 Provider 设置了共享状态的值(此时共享状态的标志变为 ready),std::future::get() 返回异步任务的值或异常(如果发生了异常)。

1.1 valid()

valid() 检查当前的 std::future 对象是否有效,即是否与某个共享状态相关联。

一个有效的 std::future 对象通常由以下三种 Provider 创建,并和某个共享状态相关联。Provider 可以是函数或者类,它们分别是:

  • std::async()
  • std::promise::get_future()
  • std::packaged_task::get_future()

需要注意的是,由 std::future 默认构造函数创建的 std::future 对象是无效的。

1.2 get()

当与该 std::future 对象相关联的共享状态标志变为 ready 后,调用 get() 函数将返回保存在共享状态中的值,如果共享状态的标志不为 ready,则调用 get() 函数会阻塞当前的调用者,而此后一旦共享状态的标志变为 ready,get() 返回 Provider 所设置的共享状态的值或异常(如果抛出了异常)。

需要注意的是,std::future 的 get() 只能调用一次,不能调用多次,因为其内部是移动语义。

在下面代码中,std::async() 用来启动一个异步任务,即自动创建一个线程并开始执行对应的线程入口函数,返回一个 std::future 对象,这个 std::future 对象里边就含有线程返回的结果(即线程入口函数所返回的结果),我们可以通过调用 std::future 对象的成员函数 get() 来获取结果。

#include <iostream>
#include <future>
using namespace std;

int mythread(int val) // 线程入口函数
{
    cout << "mythread start, " << "mythread id = " << std::this_thread::get_id() << endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(5000)); // 休息5秒
    cout << "mythread end, " << "mythread id = " << std::this_thread::get_id() << endl;
    return val;
}

int main()
{
    cout << "main start, " << "main id = " << std::this_thread::get_id() << endl;

    std::future<int> result; // 由默认构造函数创建的std::future对象是无效状态
    cout << result.valid() << endl; // 0

    result = std::async(std::launch::async, mythread, 180); // 创建一个线程并开始执行,流程并不会卡在这里
    cout << result.valid() << endl; // 1

    cout << "main continue..." << endl;

    cout << result.get() << endl; // 卡在这里等待mythread线程执行完毕,拿到结果

    cout << "main end, " << "main id = " << std::this_thread::get_id() << endl;
    return 0;
}

1.3 wait()

wait() 等待与当前 std::future 对象相关联的共享状态的标志变为 ready。

如果共享状态的标志不是 ready(此时 Provider 没有在共享状态上设置值或异常),调用该函数会被阻塞当前线程,直到共享状态的标志变为 ready。

一旦共享状态的标志变为 ready,wait() 函数返回,当前线程被解除阻塞,但是 wait() 并不读取共享状态的值或异常。

1.4 wait_for()

wait_for() 与 wait() 的功能类似,即等待与该 std::future 对象相关联的共享状态的标志变为 ready。而与 wait() 不同的是,wait_for() 可以设置一个时间段,如果共享状态的标志在该时间段结束之前没有被 Provider 设置为 ready,则调用 wait_for 的线程被阻塞,在等待了该时间段后 wait_for() 返回,返回值如下:

在这里插入图片描述

  • future_status::ready:共享状态的标志已经变为 ready,即 Provider 在共享状态上设置了值或异常。
  • future_status::timeout:超时,即在规定的时间内共享状态的标志没有变为 ready。
  • future_status::deferred:共享状态包含一个 deferred 函数。

1.4.1 future_status::timeout

#include <iostream>
#include <future>
using namespace std;

int mythread(int val) // 线程入口函数
{
    cout << "mythread start, " << "mythread id = " << std::this_thread::get_id() << endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(5000)); // 休息5秒
    cout << "mythread end, " << "mythread id = " << std::this_thread::get_id() << endl;
    return val;
}

int main()
{
    cout << "main start, " << "main id = " << std::this_thread::get_id() << endl;
    std::future<int> result = std::async(std::launch::async, mythread, 180); // 创建一个线程并开始执行,流程并不会卡在这里
    cout << "main continue..." << endl;
    std::future_status status = result.wait_for(std::chrono::seconds(1));
    // 在主线程中等待1秒,1秒后mythread线程还没有执行完毕,即mythread线程timeout
    if (status == std::future_status::timeout)
    {
        cout << "timeout" << endl;
    }
    cout << result.get() << endl; // 卡在这里等待mythread线程执行完毕,拿到结果
    cout << "main end, " << "main id = " << std::this_thread::get_id() << endl;
    return 0;
}

1.4.2 future_status::ready

#include <iostream>
#include <future>
using namespace std;

int mythread(int val) // 线程入口函数
{
    cout << "mythread start, " << "mythread id = " << std::this_thread::get_id() << endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(5000)); // 休息5秒
    cout << "mythread end, " << "mythread id = " << std::this_thread::get_id() << endl;
    return val;
}

int main()
{
    cout << "main start, " << "main id = " << std::this_thread::get_id() << endl;
    std::future<int> result = std::async(std::launch::async, mythread, 180); // 创建一个线程并开始执行,流程并不会卡在这里
    cout << "main continue..." << endl;
    std::future_status status = result.wait_for(std::chrono::seconds(60));
    // 在60秒结束之前mythread线程就已经执行完毕了,wait_for()函数直接返回,即mythread线程ready
    if (status == std::future_status::ready)
    {
        cout << "ready" << endl;
    }
    cout << result.get() << endl; // 拿到结果
    cout << "main end, " << "main id = " << std::this_thread::get_id() << endl;
    return 0;
}

1.4.3 future_status::deferred

#include <iostream>
#include <future>
using namespace std;

int mythread(int val) // 线程入口函数
{
    cout << "mythread start, " << "mythread id = " << std::this_thread::get_id() << endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(5000)); // 休息5秒
    cout << "mythread end, " << "mythread id = " << std::this_thread::get_id() << endl;
    return val;
}

int main()
{
    cout << "main start, " << "main id = " << std::this_thread::get_id() << endl;
    std::future<int> result = std::async(std::launch::deferred, mythread, 180);
    cout << "main continue..." << endl;
    // 如果async()的启动策略被设置为std::launch::deferred,那么主线程并不会等待60秒,wait_for()函数会直接返回,下面判断成立
    std::future_status status = result.wait_for(std::chrono::seconds(60));
    if (status == std::future_status::deferred)
    {
        cout << "deferred" << endl;
    }
    cout << result.get() << endl; // 在主线程中去执行mythread()函数,拿到结果
    cout << "main end, " << "main id = " << std::this_thread::get_id() << endl;
    return 0;
}

1.5 share()

share() 返回一个 std::shared_future 对象。如果某个 std::future 对象调用了 share() 函数,那么该 std::future 对象就不和任何共享状态相关联了(变为空),因为其内部是移动语义。

2.std::shared_future

std::shared_future 也是个类模板,与 std::future 的作用相同。

不同之处在于,std::shared_future 可复制,并且多个 std::shared_future 对象可以关联同一个共享状态,其源码如下所示。

在这里插入图片描述

std::shared_future 的 get() 可以调用多次。

#include <iostream>
#include <future>
using namespace std;

int mythread(int val) // 线程入口函数
{
    cout << "mythread start, " << "mythread id = " << std::this_thread::get_id() << endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(5000)); // 休息5秒
    cout << "mythread end, " << "mythread id = " << std::this_thread::get_id() << endl;
    return val;
}

int main()
{
    cout << "main start, " << "main id = " << std::this_thread::get_id() << endl;
    std::shared_future<int> ret = std::async(std::launch::async, mythread, 180); // 创建一个线程并开始执行,流程并不会卡在这里
    cout << "main continue..." << endl;
    cout << ret.get() << endl; // 卡在这里等待mythread线程执行完毕,拿到结果
    cout << ret.get() << endl; // 拿到结果
    cout << ret.get() << endl; // 拿到结果
    cout << "main end, " << "main id = " << std::this_thread::get_id() << endl;
    return 0;
}
#include <iostream>
#include <future>
using namespace std;

int mythread(int val) // 线程入口函数
{
    cout << "mythread start, " << "mythread id = " << std::this_thread::get_id() << endl;
    std::this_thread::sleep_for(std::chrono::milliseconds(5000)); // 休息5秒
    cout << "mythread end, " << "mythread id = " << std::this_thread::get_id() << endl;
    return val;
}

int main()
{
    cout << "main start, " << "main id = " << std::this_thread::get_id() << endl;

    std::future<int> ret; // 由默认构造函数创建的std::future对象是无效状态
    cout << ret.valid() << endl; // 0

    ret = std::async(std::launch::async, mythread, 180); // 创建一个线程并开始执行,流程并不会卡在这里
    cout << ret.valid() << endl; // 1

    cout << "main continue..." << endl;
    
    std::shared_future<int> sret(ret.share()); // 移动构造sret,ret变为空
    
    cout << sret.get() << endl; // 卡在这里等待mythread线程执行完毕,拿到结果
    cout << sret.get() << endl; // 拿到结果
    cout << sret.get() << endl; // 拿到结果

    cout << "main end, " << "main id = " << std::this_thread::get_id() << endl;
    return 0;
}

3.参考资料

https://www.cnblogs.com/haippy/p/3280643.html

https://blog.csdn.net/luoshabugui/article/details/108985042

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值