《探索C++多线程》:future源码(二)

来源:互联网 发布:修改linux ip 编辑:程序博客网 时间:2024/05/16 19:53

接上一篇文章:《探索C++多线程》:future源码(一),在本文中将对std::promise、std::packaged_task进行分析。

std::promise

        promise对象可以保存某一T类型的值,该值可以被future对象(可能在另一个线程中)获取,因此promise也提供了一种同步手段。

        在构造时,promise对象与一个新的共享状态(通常是std::future)相关联,他们可以存储T类型的值或从std::exception派生的异常。共享状态可以通过调用成员get_future来与future相关联,调用后,两个对象共享同一个状态:

        1、promise对象是异步的提供者,并在某一时刻为共享状态设置值;

        2、future对象是异步返回共享状态的值,它可以获取异步状态的值(必要时阻塞等待状态就绪);

我们来看一个例子:

#include <iostream>       // std::cout#include <functional>     // std::ref#include <thread>         // std::thread#include <future>         // std::promise, std::futureusing namespace std;void print_int(future<int>& fut) {    int x = fut.get();      // 阻塞获取,当在另一个线程中调用了promise::set_value()后,不再阻塞,并立即返回共享状态的值    cout << "value: " << x << '\n';}int main() {    promise<int> prom;                      // 创建promise对象    future<int> fut = prom.get_future();    // 与future关联    thread th1(print_int, ref(fut));        // 将future对象传到一个线程中    prom.set_value(10);                     // 设置值    // 与future同步    th1.join();    getchar();    return 0;}
在上述代码中,pormise对象调用get_future()方法,返回与之关联的future。

std::promise::get_future()

        该方法返回一个与promise对象共享状态相关联的future对象,返回的future对象,可以通过promise对象来访问共享状态的值或异常,每个promise对象共享状态返回一个future对象。

        调用了此方法后,promise对象将在某一时刻(通过设置值或异常)使共享状态准备就绪,否则,它将在自动析构包含future_error类型异常的情况下准备就绪。
std::promise::set_value()

        设置共享状态的值,若一个future对象关联了同一共享状态,并且调用了future::get()来阻塞的获取值时,经调用promise::set_value()后将不再阻塞并返回一个值。

std::promise::set_value_at_thread_exit()

        设置共享状态的值,但并不立即将共享状态的标志设置为ready;相反地,是在线程退出时设置为就绪状态。如果某个future对象与promise对象的共享状态相关联,并且该future对象正在调用get(),则会被阻塞,当线程退出时,不再阻塞并且返回共享状态的值。

std::packaged_task()

        包装了一个可调用的目标,并且允许异步的获取其结果。与std::function类似,但其结果将自动的传递给future对象(可以在另一个线程中调用future::get()获取该结果)。

我们来看一个例子:

#include <iostream>     // std::cout#include <future>       // std::packaged_task, std::future#include <chrono>       // std::chrono::seconds#include <thread>       // std::thread, std::this_thread::sleep_forusing namespace std;// 被包装的函数:秒倒数计数int countdown(int from, int to) {    for (int i = from; i != to; --i) {        cout << i << '\n';        this_thread::sleep_for(chrono::seconds(1));    }    cout << "Lift off!\n";    return from - to;}int main() {    packaged_task<int(int, int)> tsk(countdown);    // 建立 packaged_task    future<int> ret = tsk.get_future();             // 获取 future 对象    thread th(move(tsk), 10, 0);                    // 启动从10到0向下计数的线程    int value = ret.get();                          // 阻塞的等待任务结束,并获取返回值    cout << "The countdown lasted for " << value << " seconds.\n";    th.join();    return 0;}

packaged_task对象内部包含两个元素:

        1、被包装的任务:如函数指针,指向成员或函数对象的指针;

        2、存储共享状态:用于存储任务的返回值,可以通过future::get()来异步访问共享状态的值。

std::packaged_task::get_future()

我们来看一段代码:

#include <iostream>     // std::cout#include <utility>      // std::move#include <future>       // std::packaged_task, std::future#include <thread>       // std::threadusing namespace std;int triple (int x) {    return x * 3;}int main () {    packaged_task<int(int)> tsk (triple);   // 包装任务    future<int> fut = tsk.get_future();     // 获取 future 对象    thread(move(tsk), 33).detach();    // 建立线程,并调用任务    int value = fut.get();                  // 阻塞等待任务完成,并获取结果    cout << "The triple of 33 is " << value << ".\n";    return 0;}
在上述代码中,主线程与任务线程剥离,任务线程交由系统管理,主线程调用fut.get()将阻塞地等待任务线程计算完毕,才能获取到共享状态的值。

std::packaged_task::reset()

同样的,我们来看一段代码:

#include <iostream>     // std::cout#include <utility>      // std::move#include <future>       // std::packaged_task, std::future#include <thread>       // std::threadusing namespace std;int triple(int x) {    return x * 3;}int main() {    packaged_task<int(int)> tsk(triple);    future<int> fut = tsk.get_future();    tsk(33);    cout << "The triple of 33 is " << fut.get() << ".\n";    // 再次使用同一个 packaged_task 对象    tsk.reset();    fut = tsk.get_future();    thread(move(tsk), 99).detach();    cout << "Thre triple of 99 is " << fut.get() << ".\n";    getchar();    return 0;}
重置任务:在保持同一包装任务的同时,使用新的共享状态来重置任务对象,这允许再次调用存储任务。

2 0
原创粉丝点击