这一节学习C++里面的future,在多线程里面, 如果等待线程只打算等待一次,那么当条件为 true 时它就不会再等待这个条件变量了,条件变量未必是同步机制的最佳选择。如果所等待的条件是一个特定数据块的可用性时,这尤其正确。在这个场景中,使用期值(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_ptr和std::shared_ptr。std::future的实例只能与一个指定事件相关联,而std::shared_future的实例就能关联多个事件。后者的实现中,所有实例会在同时变为就绪状态,并且他们可以访问与事件相关的任何数据。这种数据关联与模板有关,比如std::unique_ptr 和std::shared_ptr的模板参数就是相关联的数据类型。在与数据无关的地方,可以使用std::future
从后台任务中返回值
假设你有一个长期运行的计算,预期最终将得到一个有用的结果,但是现在,你还不需要这个值。你可以启动一个新的线程来执行该计算,但这也意味着你必须注意将结果传回来,因为 std:: thread 并没有提供直接的机制来这样做。这就是 std:: async 函数模板(同样声明于< future>头文件中)的由来。
在不需要立刻得到结果的时候,你可以使用 std::async 来启动一个异步任务( asynchronous task )。 std::async 返回一个 std:: future 对象,而不是给你一个std: : thread 对象让你在上面等待, std:: future 对象最终将持有函数的返回值。当你需要这个值时,只要在 future 上调用 get (),线程就会阻塞直到“ture 就绪,然后返回该值。如下:
#include#include int find_the_answer_to_ltuae(); void do_other_stuff(); int main() { std::future the_answer=std::async(find_the_answer_to_ltuae); do_other_stuff(); std::cout<<"The answer is "< 分析: 与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 templatepromise (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// std::cout #include // std::thread #include // std::promise, std::future std::promise prom; void print_global_promise () { std::future 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 (); // 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,然后才能获取共享状态的值。
#includestd::promise::set_exception// std::cout #include // std::ref #include // std::thread #include // std::promise, std::future void print_int(std::future & fut) { int x = fut.get(); // 获取共享状态的值. std::cout << "value: " << x << 'n'; // 打印 value: 10. } int main () { std::promise prom; // 生成一个 std::promise 对象. std::future fut = prom.get_future(); // 和 future 关联. std::thread t(print_int, std::ref(fut)); // 将 future 交给另外一个线程t. prom.set_value(10); // 设置共享状态的值, 此处和线程t保持同步. t.join(); return 0; } 为 promise 对象设置异常,此后 promise 的共享状态变标志变为 ready,例子如下,线程1从终端接收一个整数, 线程 2 将该整数打印出来,如果线程 1 接收一个非整数,则为 promise 设置一个异常(failbit) ,线程 2 在 std::future::get 是抛出该异常。
#include// std::cin, std::cout, std::ios #include // std::ref #include // std::thread #include // std::promise, std::future #include // std::exception, std::current_exception void get_int(std::promise & 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 & 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 prom; std::future 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; }



