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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/hujingshuang/article/details/71412617

接上一篇文章:《探索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::future

using 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_for

using 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::thread

using 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::thread

using 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;
}
重置任务:在保持同一包装任务的同时,使用新的共享状态来重置任务对象,这允许再次调用存储任务。

猜你喜欢

转载自blog.csdn.net/hujingshuang/article/details/71412617