C++ std::future

这篇具有很好参考价值的文章主要介绍了C++ std::future。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

std::future是用来接收一个线程的执行结果的,并且是一次性的。

共享状态shared state

future可以关联一个共享状态,共享状态是用来储存要执行结果的。这个结果是async、promise、packaged_task设置的,且这个结果只能设置一次。

C++ std::future,c++,开发语言

创建future

创建future有四种方式

  1.  直接构造函数创建,这种创建的future没用共享状态。
    std::future<DataType> future;
  2. 调用std::async,返回一个future对象,返回的future有共享状态。std::async是执行一个异步任务,这个任务会放到线程池中的线程执行,并通过future返回执行结果。通常异步任务优先考虑使用std::async,而不是std::thread。
    auto future = std::async([]{
        return DataType{5};
    });
    std::cout <<"future is Valid:" << future.valid() 
              << " , async Result:" << future.get().n << std::endl;
  3. 创建std::promise,然后调用std::promise::get_future获取future对象,返回的future有共享状态。promise可以设置一个值传递给future,也可以设置一个异常传递给future。promise的set_value和set_exception 只能调用一次,调第二次时会抛出异常。如果promise在析构前都未设置结果,调用future::get获取结果会抛出异常。
        {
            std::cout << "------------- promise -----------------\n";
            std::promise<DataType> promise;
            promise.set_value({5});
            // promise.set_value({6}); //set_value只能调用一次,再次调用会抛出异常
            auto future = promise.get_future();
            // auto future2 = promise.get_future(); //get_future只能调用一次,再次调用会抛出异常
            std::cout <<"future is Valid:" << future.valid() << " , async Result:" << future.get().n << std::endl;
        }
        {
            std::cout << "------------- promise exception -----------------\n";
            std::promise<DataType> promise;
    
            try {
                throw std::runtime_error("error");
            } catch (...) {
                // 如果调用了 promise.set_value ,则不能调用 promise.set_exception ,
                // 否则 promise.set_exception 会抛出异常
                promise.set_exception(std::current_exception());
            }
            auto future = promise.get_future();
            try {
                auto data =  future.get();
                std::cout <<"future is Valid:" << future.valid() << " , async Result:" << data.n << std::endl;
            } catch (const std::exception& e) {
                std::cout << e.what() << std::endl;
            }
        }
    
  4. 创建std::packaged_task,然后调用get_futurestd::packaged_task::get_future获取future对象,返回的future有共享状态。
    std::cout << "------------- packaged -----------------\n";
    std::packaged_task<DataType(int)> task([](int a) {
        return DataType{a};
    });
    auto future = task.get_future();
    // auto future2 = task.get_future(); //get_future只能调用一次,再次调用会抛出异常
    task(5);
    std::cout <<"future is Valid:" << future.valid()
              << " , async Result:" << future.get().n << std::endl;

future状态

可以通过函数future::valid可以判断future是否有共享状态。如果有共享状态可以进一步通过函数future::wait_for(0s)来判读结果是否已经设置。wait_for有三个返回值:

  1. future_status::deferred 表示结果还未设置
  2. future_status::ready 表示结果已经设置
  3. future_status::timeout 表示超时

获取结果

通过future::get函数可以获取future的结果,调用get函数需要注意以下几点:

  1.  只有有共享状态的future才能获取结果,如果没有共享状态会抛出异常。
  2. 如果获取数据时future结果已经设置好,则会立即返回。
  3. 如果future状态还未设置好,则会挂起线程一直等待,直到结果被设置。如果相应async、promise、packaged_task到结束都一直未设置状态,则会抛出异常。
  4. get函数只能调用一次,调用完成后则会删除共享状态,再次调用get函数,会因为没有共享状态而抛出异常。

也可以调用wait,wait_for, wait_until等带结果被设置。

多线程调用future

获取future结果时,可以先判断future是否有共享状态,然后再调用get函数获取结果,代码如下:

        if (future.valid())
        {
            future.get();
        }

上面代码在单线程调用没有问题,如果在线程同时调用则会有问题,多个线程会同时判断future有共享状态,因此同时有多个线程调用get函数,而get函数只能被调用一次,从而引发异常。

可以通过加锁解决这个问题,但这并不是好的方法。更优雅的方法是通过future::share函数,为每个线程创建一个std::shared_future。future调用完share将不再拥有共享状态,如下:

C++ std::future,c++,开发语言

如果future本身没有共享状态,调用share会生成一个没有共享状态的shared_future。

shared_future调用get函数时并不会清除共享状态,所以shared_future可以多次调用get函数。

示例代码:

    std::promise<DataType> promise;
    auto future = promise.get_future();
    auto shadedFuture = future.share();
    std::cout << "future is valid: " << future.valid() << std::endl;
    
    std::thread threads[4];
    for (int i = 0; i < 4; ++i)
    {
        threads[i] = std::thread([future=shadedFuture](int i){
            std::cout << std::to_string(i) + ", future is Valid:" + std::to_string(future.valid())
                             + " , async Result:" + std::to_string(future.get().n) + "\n";
        }, i);
    }
    
    std::this_thread::sleep_for(std::chrono::milliseconds{100});
    promise.set_value({5});
    
    for (int i = 0; i < 4; ++i)
    {
        threads[i].join();
    }
    std::cout << std::endl;

future析构阻塞

当future是由async创建的,且future/shared_future是最后一个指向共享状态的future,如果async还在执行,则future在析构时会阻塞当前线程,等待async执行完成。文章来源地址https://www.toymoban.com/news/detail-708943.html

到了这里,关于C++ std::future的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

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

    C++11标准库增加了获取线程返回值的方法,头文件为future,主要包括 future 、 promise 、 packaged_task 、 async 四个类。 那么,了解一下各个类的构成以及功能。 future是一个模板类,它是传输线程返回值(也称为 共享状态 )的媒介,也可以理解为线程返回的结果就安置在future中。

    2024年02月02日
    浏览(43)
  • C++面试八股文:用过std::set/std::map吗?

    某日二师兄参加XXX科技公司的C++工程师开发岗位第27面: 面试官:用过 std::set/std::map 吗? 二师兄:用过。 面试官:能介绍一下二者吗? 二师兄: std::set 是一个有序的集合,其中的元素是唯一的,即每个元素只能出现一次。一般用于去重和自动排序。 二师兄: std::map 同样是

    2024年02月11日
    浏览(55)
  • [C++] std::tuple

    ​ std::tuple是C++11新标准引入的一个类模板,又称为元组,是一个固定大小的异构值集合,由std::pair泛化而来。pair可以看作是tuple的一种特殊情况,成员数目限定为两个。tuple可以有任意个成员数量,但是每个确定的tuple类型的成员数目是固定的。 ​ 从概念上讲,它们类似于

    2024年02月12日
    浏览(40)
  • C++面试八股文:std::vector和std::list,如何选择?

    某日二师兄参加XXX科技公司的C++工程师开发岗位第24面: 面试官: list 用过吗? 二师兄:嗯,用过。 面试官:请讲一下 list 的实现原理。 二师兄: std::list 被称为双向链表,和C中手写双向链表本质上没有大的区别。 list 对象中有两个指针,一个指向上一个节点( node ),一

    2024年02月10日
    浏览(39)
  • C++的std::vector

    std::vector 是C++标准库中的一个 序列容器 ,它 封装了动态大小数组的行为 。 std::vector 允许你在运行时 动态地添加和删除元素 ,自动管理存储空间的分配和释放。由于其灵活性和易用性, std::vector 在C++程序中被广泛使用,特别是在需要存储一系列元素时。 动态大小:std::v

    2024年02月21日
    浏览(27)
  • 【C++】详解std::thread

    2023年9月10日,周日下午开始 2023年9月10日,周日晚上23:35完成 虽然这篇博客我今天花了很多时间去写,但是我对std::thread有了一个完整的认识 不过有些内容还没完善,以后有空再更新.... 目录 头文件 类的成员 类型 方法 (constructor) terminate called without an active exception是什么?

    2024年02月09日
    浏览(43)
  • C++ 基础: std::string

    std::string是C++标准库中的一个类,用于表示可变长度的字符串。它是由字符类型(通常是char)的数组实现的,并提供了许多字符串操作函数。 std::string的构造函数有多种形式,可以用于初始化空字符串、从字符数组或另一个字符串中复制字符串、从指定长度的字符数组或另一

    2024年02月10日
    浏览(45)
  • [C++] std::optional与RVO:最高效的std::optional实践与探究

    在cppreference中,是这么介绍RVO的 In a return statement, when the operand is the name of a non-volatile object with automatic storage duration, which isn\\\'t a function parameter or a catch clause parameter, and which is of the same class type (ignoring cv-qualification) as the function return type. This variant of copy elision is known as NRVO, \\\"name

    2024年02月10日
    浏览(30)
  • C++编程之 std::forward

    std::forward 是一个 C++11 中的模板函数,其主要作用是在模板函数或模板类中,将一个参数以“原样”(forward)的方式转发给另一个函数。通常情况下,该函数被用于实现完美转发(perfect forwarding)。 完美转发是指,一个函数或类模板可以将其参数原封不动地转发给另一个函数

    2024年02月14日
    浏览(26)
  • C++ std::tie函数详解

    C++中std::tie函数的作用就是从元素引用中生成一个tuple元组,其在头文件tuple中定义,其函数原型如下: 其中参数 args 为构造 tuple 所用的零或更多左值参数。其返回值为含左值引用的std::tuple对象。 1. 解包 tuple 和 pair std::tie 可以用于解包 tuple 和 pair,因为 std::tuple 拥有从 pai

    2024年02月05日
    浏览(37)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包