C++笔记--future

Posted ljt2724960661

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++笔记--future相关的知识,希望对你有一定的参考价值。

         这一节学习C++里面的future,在多线程里面, 如果等待线程只打算等待一次,那么当条件为 true 时它就不会再等待这个条件变量了,条件变量未必是同步机制的最佳选择。如果所等待的条件是一个特定数据块的可用性时,这尤其正确。在这个场景中,使用期值(future)可能会更合适。<future> 头文件中包含了以下几个类和函数:

Providers 类:std::promise, std::package_task
Futures 类:std::future, std::shared_future.
Providers 函数:std::async()
其他类型:std::future_error, std::future_errc, std::future_status, std::launch.

使用 Future 等待一次性事件

         标准库模型将这种一次性事件称为期望(future)。当一个线程需要等待一个特定的一次性事件时,在某种程度上来说它就需要知道这个事件在未来的表现形式。之后,这个线程会周期性(较短的周期)的等待或检查,事件是否触发(检查信息板);在检查期间也会执行其他任务(品尝昂贵的咖啡)。另外,在等待任务期间它可以先执行另外一些任务,直到对应的任务触发,而后等待期望的状态会变为就绪(ready)。一个“期望”可能是数据相关的(比如,你的登机口编号),也可能不是。当事件发生时(并且期望状态为就绪),这个“期望”就不能被重置。

        在C++标准库中,有两种“期望”,使用两种类型模板实现,声明在头文件中: 唯一期望(unique futures)(std::future<>)和共享期望(shared futures)(std::shared_future<>)。这是仿照std::unique_ptrstd::shared_ptrstd::future的实例只能与一个指定事件相关联,而std::shared_future的实例就能关联多个事件。后者的实现中,所有实例会在同时变为就绪状态,并且他们可以访问与事件相关的任何数据。这种数据关联与模板有关,比如std::unique_ptr 和std::shared_ptr的模板参数就是相关联的数据类型。在与数据无关的地方,可以使用std::future<void>std::shared_future<void>的特化模板。虽然,希望用于线程间的通讯,但是“期望”对象本身并不提供同步访问。当多个线程需要访问一个独立“期望”对象时,他们必须使用互斥量或类似同步机制对访问进行保护,多个线程会对一个std::shared_future<>实例的副本进行访问,而不需要期望同步,即使他们是同一个异步结果。最基本的一次性事件,就是一个后台运行出的计算结果。

    从后台任务中返回值

        假设你有一个长期运行的计算,预期最终将得到一个有用的结果,但是现在,你还不需要这个值。你可以启动一个新的线程来执行该计算,但这也意味着你必须注意将结果传回来,因为 std:: thread 并没有提供直接的机制来这样做。这就是 std:: async 函数模板(同样声明于< future>头文件中)的由来。

         在不需要立刻得到结果的时候,你可以使用 std::async 来启动一个异步任务( asynchronous task )。 std::async 返回一个 std:: future 对象,而不是给你一个std: : thread 对象让你在上面等待, std:: future 对象最终将持有函数的返回值。当你需要这个值时,只要在 future 上调用 get (),线程就会阻塞直到“ture 就绪,然后返回该值。如下:

#include <future>
#include <iostream>

int find_the_answer_to_ltuae();
void do_other_stuff();
int main()

  std::future<int> the_answer=std::async(find_the_answer_to_ltuae);
  do_other_stuff();
  std::cout<<"The answer is "<<the_answer.get()<<std::endl;

分析: 与std::thread 做的方式一样,std::async允许你通过添加额外的调用参数,向函数传递额外的参数。当第一个参数是一个指向成员函数的指针,第二个参数提供有这个函数成员类的具体对象(不是直接的,就是通过指针,还可以包装在std::ref中),剩余的参数可作为成员函数的参数传入。否则,第二个和随后的参数将作为函数的参数,或作为指定可调用对象的第一个参数。就如std::thread,当参数为右值(rvalues)时,拷贝操作将使用移动的方式转移原始数据。这就允许使用“只移动”类型作为函数对象和参数。

      异步任务提供者(Provider) 

       这里主要说Promise,Promise 对象可以保存某一类型 T 的值,该值可被 future 对象读取(可能在另外一个线程中),因此 promise 也提供了一种线程同步的手段。在 promise 对象构造时可以和一个共享状态(通常是std::future)相关联,并可以在相关联的共享状态(std::future)上保存一个类型为 T 的值。

std::promise 构造函数

1 default 	promise();
2 with allocator 	template <class Alloc> promise (allocator_arg_t aa, const Alloc& alloc);
3 copy [deleted] 	promise (const promise&) = delete;
4 move 	promise (promise&& x) noexcept;

1 默认构造函数,初始化一个空的共享状态。2 带自定义内存分配器的构造函数,与默认构造函数类似,但是使用自定义分配器来分配共享状态。3 拷贝构造函数,被禁用。4 移动构造函数。

栗子:

#include <iostream>       // std::cout
#include <thread>         // std::thread
#include <future>         // std::promise, std::future

std::promise<int> prom;

void print_global_promise () 
    std::future<int> fut = prom.get_future();
    int x = fut.get();
    std::cout << "value: " << x << '\\n';


int main ()

    std::thread th1(print_global_promise);
    prom.set_value(10);
    th1.join();

    prom = std::promise<int>();    // prom 被move赋值为一个新的 promise 对象.

    std::thread th2 (print_global_promise);
    prom.set_value (20);
    th2.join();

  return 0;

分析: 可以通过 get_future 来获取与该 promise 对象相关联的 future 对象,调用该函数之后,两个对象共享相同的共享状态(shared state)

         promise 对象是异步 Provider,它可以在某一时刻设置共享状态的值。future 对象可以异步返回共享状态的值,或者在必要的情况下阻塞调用者并等待共享状态标志变为 ready,然后才能获取共享状态的值。

#include <iostream>       // std::cout
#include <functional>     // std::ref
#include <thread>         // std::thread
#include <future>         // std::promise, std::future

void print_int(std::future<int>& fut) 
    int x = fut.get(); // 获取共享状态的值.
    std::cout << "value: " << x << '\\n'; // 打印 value: 10.


int main ()

    std::promise<int> prom; // 生成一个 std::promise<int> 对象.
    std::future<int> fut = prom.get_future(); // 和 future 关联.
    std::thread t(print_int, std::ref(fut)); // 将 future 交给另外一个线程t.
    prom.set_value(10); // 设置共享状态的值, 此处和线程t保持同步.
    t.join();
    return 0;

std::promise::set_exception

          为 promise 对象设置异常,此后 promise 的共享状态变标志变为 ready,例子如下,线程1从终端接收一个整数, 线程 2 将该整数打印出来,如果线程 1 接收一个非整数,则为 promise 设置一个异常(failbit) ,线程 2 在 std::future::get 是抛出该异常。

#include <iostream>       // std::cin, std::cout, std::ios
  #include <functional>     // std::ref
  #include <thread>         // std::thread
  #include <future>         // std::promise, std::future
  #include <exception>      // std::exception, std::current_exception

  void get_int(std::promise<int>& prom) 
      int x;
      std::cout << "Please, enter an integer value: ";
      std::cin.exceptions (std::ios::failbit);   // throw on failbit
      try 
          std::cin >> x;                         // sets failbit if input is not int
          prom.set_value(x);
       catch (std::exception&) 
          prom.set_exception(std::current_exception());
      
  

  void print_int(std::future<int>& fut) 
      try 
          int x = fut.get();
          std::cout << "value: " << x << '\\n';
       catch (std::exception& e) 
          std::cout << "[exception caught: " << e.what() << "]\\n";
      
  
  
  int main ()
  
      std::promise<int> prom;
      std::future<int> fut = prom.get_future();
  
      std::thread th1(get_int, std::ref(prom));
      std::thread th2(print_int, std::ref(fut));
  
      th1.join();
      th2.join();
      return 0;
  

以上是关于C++笔记--future的主要内容,如果未能解决你的问题,请参考以下文章

《C++ Concurrency in Action》读书笔记三 同步并发操作

C++并发学习笔记

蓝桥ROS机器人之现代C++学习笔记7.3 期物

C++ 异步编程笔记 (ISO C++ 17)

ctf-misc-150分登机牌(150分

使用std::future监控线程执行类成员函数(c++)? [复制]