c++ future库

  • providers future 和 智能指针有点类似
    future对应智能指针;
    providers对应智能指针指向的存储区域;
    当所有与shared state关联的future对象都销毁时,shared state也会销毁。

1.Providers

1.1 promise

template <class T>  promise;
template <class R&> promise<R&>;     // specialization : T is a reference type (R&)
template <>         promise<void>;   // specialization : T is void
  • promise存储的T类型的值,可以被future对象获取。promise提供了一个同步点。
  • 只有当所有的连接到promise的future对象都销毁时,promise对象才会被析构
  • 构造函数如下
//default (1)   
promise();

//with allocator (2)    
template <class Alloc> promise (allocator_arg_t aa, const Alloc& alloc);

//copy [deleted] (3)    
promise (const promise&) = delete;

//move (4)  
promise (promise&& x) noexcept;
// promise constructors
#include <iostream>       // std::cout
#include <functional>     // std::ref
#include <memory>         // std::allocator, std::allocator_arg
#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';
}

int main ()
{
  std::promise<int> foo;
  std::promise<int> bar = std::promise<int>(std::allocator_arg,std::allocator<int>());

  std::future<int> fut = bar.get_future();

  std::thread th (print_int, std::ref(fut));

  bar.set_value (20);  // fulfill promise
                       // (synchronizes with getting the future)

  th.join();
  return 0;
}
  • get_future
    返回连接到promise对象共享状态的future 对象。
    当该函数调用时,promise承诺在未来某个时间点(通过设置值或者异常)使它的共享状态可用。
    当设置好值或异常时,future对象就可以获取该共享状态的值。
future<T> get_future();
  • set_value
    在共享状态里存储值,此时shared state变为可用。
    如果此时有一个future对象正在等待此状态可用(调用future::get),它会解锁并返回该值。
// generic template (1) 
void set_value (const T& val);
void set_value (T&& val);

//specializations (2)   
void promise<R&>::set_value (R& val);   // when T is a reference type (R&)
void promise<void>::set_value (void);   // when T is void
  • set_exception
    只不过将值变为了exception。其他过程跟set_value一致。get()获得该异常时,会将异常抛出。
void set_exception (exception_ptr p);
// promise::set_exception
#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 (print_int, std::ref(fut));
  std::thread th2 (get_int, std::ref(prom));

  th1.join();
  th2.join();
  return 0;
}
  • set_value_at_thread_exit
    设置值后不会立马将shared state变为可用,要直到线程退出时才可用。
    在设置值到线程退出期间,任何修改此shared state值的操作都会抛出错误promise_already_satisfied。
  • set_exception_at_thread_exit
    类似

1.2 packaged_task——同步机制和promise一致

  • packaged_task封装一个可调用的对象,其返回值可以被获取。
  • 包含两个元素
    1)一个任务。是一个可调用对象(函数指针、函数对象指针),调用签名(call signature):参数类型是Args,返回Ret类型。
    2)一个共享状态shared state。存储调用任务的结果,可以被future获取。
template <class T> packaged_task;     // undefined
template <class Ret, class... Args> class packaged_task<Ret(Args...)>;
  • 构造函数
//default (1)   
packaged_task() noexcept;

//initialization (2)    
template <class Fn>
  explicit packaged_task (Fn&& fn);

//with allocator (3)    
template <class Fn, class Alloc>
  explicit packaged_task (allocator_arg_t aa, const Alloc& alloc, Fn&& fn);

//copy [deleted] (4)    
packaged_task (packaged_task&) = delete;

//move (5)  
packaged_task (packaged_task&& x) noexcept;
// packaged_task construction / assignment
#include <iostream>     // std::cout
#include <utility>      // std::move
#include <future>       // std::packaged_task, std::future
#include <thread>       // std::thread

int main ()
{
  std::packaged_task<int(int)> foo;                          // default-constructed
  std::packaged_task<int(int)> bar ([](int x){return x*2;}); // initialized

  foo = std::move(bar);                                      // move-assignment

  std::future<int> ret = foo.get_future();  // get future

  std::thread(std::move(foo),10).detach();  // spawn thread and call task

  // ...

  int value = ret.get();                    // wait for the task to finish and get result

  std::cout << "The double of 10 is " << value << ".\n";

  return 0;
}
  • valid
    默认初始化的对象返回false。
bool valid() const noexcept;
  • get_future
    当packaged_task对象的任务被调用时,shared state会被设置值或异常,此时future就可以获取该值。
  • make_ready_at_thread_exit——也是调用运算符
    类似于operator(),在线程退出设置shared state可用。跟promise相应的成员函数类似
void make_ready_at_thread_exit (args... args);
  • reset
// packaged_task::get_future
#include <iostream>     // std::cout
#include <utility>      // std::move
#include <future>       // std::packaged_task, std::future
#include <thread>       // std::thread

// a simple task:
int triple (int x) { return x*3; }

int main ()
{
  std::packaged_task<int(int)> tsk (triple); // package task

  std::future<int> fut = tsk.get_future();
  tsk(33);
  std::cout << "The triple of 33 is " << fut.get() << ".\n";

  // re-use same task object:
  tsk.reset();
  fut = tsk.get_future();
  std::thread(std::move(tsk),99).detach();
  std::cout << "Thre triple of 99 is " << fut.get() << ".\n";

  return 0;
}

2.Futures

2.1 future

  • future对象关联到shared state,在调用如下函数时被构造:
    1)async
    2)promise::get_future
    3)packaged_task::get_future
  • 在一个有效的future对象上调用futuer::get会阻塞其线程,直到provider让shared state变得可用(设置值或者异常)。
  • 移动赋值
//move (1)  
future& operator= (future&& rhs) noexcept;

//copy [deleted] (2)    
future& operator= (const future&) = delete;
// future::operator=
#include <iostream>       // std::cout
#include <future>         // std::async, std::future

int get_value() { return 10; }

int main ()
{
  std::future<int> fut;           // default-constructed
  fut = std::async (get_value);   // move-assigned

  std::cout << "value: " << fut.get() << '\n';

  return 0;
}
  • share
    当前的future对象的shared state不再有效。
shared_future<T> share();
// 类似于采用移动构造函数
shared_future<T>(std::move(*this)
  • get
    如果shared state不可用时,阻塞等待。
    可用时,线程解除阻塞,返回并释放shared state。使得future对象变为无效。
    该函数对每个future shared state只能调用一次。

  • valid
    检查future对象是否和shared state关联

  • wait
    阻塞的效果和get一样,只不过解除阻塞后不会读取shared state的值。

// future::wait
#include <iostream>       // std::cout
#include <future>         // std::async, std::future
#include <chrono>         // std::chrono::milliseconds

// a non-optimized way of checking for prime numbers:
bool is_prime (int x) {
  for (int i=2; i<x; ++i) if (x%i==0) return false;
  return true;
}

int main ()
{
  // call function asynchronously:
  std::future<bool> fut = std::async (is_prime,194232491); 

  std::cout << "checking...\n";
  fut.wait();

  std::cout << "\n194232491 ";
  if (fut.get())      // guaranteed to be ready (and not block) after wait returns
    std::cout << "is prime.\n";
  else
    std::cout << "is not prime.\n";

  return 0;
}
  • wait_for / wait_util
    和wait一样
// future::wait_for
#include <iostream>       // std::cout
#include <future>         // std::async, std::future
#include <chrono>         // std::chrono::milliseconds

// a non-optimized way of checking for prime numbers:
bool is_prime (int x) {
  for (int i=2; i<x; ++i) if (x%i==0) return false;
  return true;
}

int main ()
{
  // call function asynchronously:
  std::future<bool> fut = std::async (is_prime,700020007); 

  // do something while waiting for function to set future:
  std::cout << "checking, please wait";
  std::chrono::milliseconds span (100);
  while (fut.wait_for(span)==std::future_status::timeout)
    std::cout << '.';

  bool x = fut.get();

  std::cout << "\n700020007 " << (x?"is":"is not") << " prime.\n";

  return 0;
}

2.2 shared_future

  • 与future不同的是,可以被赋值,可以多个shared_future共享一个shared state,shared state的值可以被获取多次。
  • get
    和future一样,只不过当shared state可用时,不会释放shared state。

3.其他类型

3.1 launch

  • 设置async的策略
enum class launch;
  • launch::async
    新建一个线程调用该函数,并在访问shared state对返回值进行同步。
  • launch::deferred(延迟调用)
    在访问shared state时才创建线程调用函数(get或wait)。
// launch::async vs launch::deferred
#include <iostream>     // std::cout
#include <future>       // std::async, std::future, std::launch
#include <chrono>       // std::chrono::milliseconds
#include <thread>       // std::this_thread::sleep_for

void print_ten (char c, int ms) {
  for (int i=0; i<10; ++i) {
    std::this_thread::sleep_for (std::chrono::milliseconds(ms));
    std::cout << c;
  }
}

int main ()
{
  std::cout << "with launch::async:\n";
  std::future<void> foo = std::async (std::launch::async,print_ten,'*',100);
  std::future<void> bar = std::async (std::launch::async,print_ten,'@',200);
  // async "get" (wait for foo and bar to be ready):
  foo.get();
  bar.get();
  std::cout << "\n\n";

  std::cout << "with launch::deferred:\n";
  foo = std::async (std::launch::deferred,print_ten,'*',100);
  bar = std::async (std::launch::deferred,print_ten,'@',200);
  // deferred "get" (perform the actual calls):
  foo.get();
  bar.get();
  std::cout << '\n';

  return 0;
}
with launch::async:
*@**@**@**@**@*@@@@@

with launch::deferred:
**********@@@@@@@@@@

3.2 future_status

ready / timeout / deffered

3.3 future_errc

broken_promise/ future_already_retrieved /promise_already_satisfied/ no_state

3.4 future_error 异常类

  • 继承自logic_error
class future_error : public logic_error;

4.函数Providers——async

  • 调用fn,不等待fn执行完成,直接返回
    函数返回值可以通过future对象读取(future::get)
//unspecified policy (1)    
template <class Fn, class... Args>
  future<typename result_of<Fn(Args...)>::type>
    async (Fn&& fn, Args&&... args);

//specific policy (2)   
template <class Fn, class... Args>
  future<typename result_of<Fn(Args...)>::type>
    async (launch policy, Fn&& fn, Args&&... args);

5.函数future_category

const error_category& future_category() noexcept;
// std::future_category example:
#include <iostream>     // std::cerr
#include <future>       // std::promise, std::future_error, std::future_category

int main ()
{
  std::promise<int> prom;

  try {
    prom.get_future();
    prom.get_future();   // throws a std::future_error of the future category
  }
  catch (std::future_error& e) {
    if (e.code().category() == std::future_category())
    std::cerr << "future_error of the future category thrown\n";
  }

  return 0;
}