c++11 多线程4

std::promise 类介绍
<future> 头文件中包含了以下几个类和函数:


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


std::promise 类介绍


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


可以通过 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 构造函数
默认构造函数,初始化一个空的共享状态。
带自定义内存分配器的构造函数,与默认构造函数类似,但是使用自定义分配器来分配共享状态。
拷贝构造函数,被禁用。
移动构造函数。
另外,std::promise 的 operator= 没有拷贝语义,即 std::promise 普通的赋值操作被禁用,operator= 只有 move 语义,所以 std::promise 对象是禁止拷贝的。


#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;
}


std::promise::set_value_at_thread_exit 介绍


设置共享状态的值,但是不将共享状态的标志设置为 ready,当线程退出时该 promise 对象会自动设置为 ready。如果某个 std::future 对象与该 promise 对象的共享状态相关联,并且该 future 正在调用 get,则调用 get 的线程会被阻塞,当线程退出时,调用 future::get 的线程解除阻塞,同时 get 返回 set_value_at_thread_exit 所设置的值。注意,该函数已经设置了 promise 共享状态的值,如果在线程结束之前有其他设置或者修改共享状态的值的操作,则会抛出 future_error( promise_already_satisfied )。


std::promise::swap 介绍


交换 promise 的共享状态。




2.package_task.


std::packaged_task 包装一个可调用的对象,并且允许异步获取该可调用对象产生的结果,从包装可调用对象意义上来讲,std::packaged_task 与 std::function 类似,只不过 std::packaged_task 将其包装的可调用对象的执行结果传递给一个 std::future 对象(该对象通常在另外一个线程中获取 std::packaged_task 任务的执行结果)。


std::packaged_task 对象内部包含了两个最基本元素,一、被包装的任务(stored task),任务(task)是一个可调用的对象,如函数指针、成员函数指针或者函数对象,二、共享状态(shared state),用于保存任务的返回值,可以通过 std::future 对象来达到异步访问共享状态的效果


#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


// count down taking a second for each value:
int countdown (int from, int to) {
    for (int i=from; i!=to; --i) {
        std::cout << i << '\n';
        std::this_thread::sleep_for(std::chrono::seconds(1));
    }
    std::cout << "Finished!\n";
    return from - to;
}


int main ()
{
    std::packaged_task<int(int,int)> task(countdown); // 设置 packaged_task
    std::future<int> ret = task.get_future(); // 获得与 packaged_task 共享状态相关联的 future 对象.


    std::thread th(std::move(task), 10, 0);   //创建一个新线程完成计数任务.


    int value = ret.get();                    // 等待任务完成并获取结果.


    std::cout << "The countdown lasted for " << value << " seconds.\n";


    th.join();
    return 0;
}




#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; // 默认构造函数.


    // 使用 lambda 表达式初始化一个 packaged_task 对象.
    std::packaged_task<int(int)> bar([](int x){return x*2;});


    foo = std::move(bar); // move-赋值操作,也是 C++11 中的新特性.


    // 获取与 packaged_task 共享状态相关联的 future 对象.
    std::future<int> ret = foo.get_future();


    std::thread(std::move(foo), 10).detach(); // 产生线程,调用被包装的任务.


    int value = ret.get(); // 等待任务完成并获取结果.
    std::cout << "The double of 10 is " << value << ".\n";


return 0;
}




与 std::promise 类似, std::packaged_task 也禁用了普通的赋值操作运算,只允许 move 赋值运算。


std::packaged_task::valid 介绍


检查当前 packaged_task 是否和一个有效的共享状态相关联,对于由默认构造函数生成的 packaged_task 对象,该函数返回 false,除非中间进行了 move 赋值操作或者 swap 操作。




#include <iostream>     // std::cout
#include <utility>      // std::move
#include <future>       // std::packaged_task, std::future
#include <thread>       // std::thread


// 在新线程中启动一个 int(int) packaged_task.
std::future<int> launcher(std::packaged_task<int(int)>& tsk, int arg)
{
    if (tsk.valid()) {
        std::future<int> ret = tsk.get_future();
        std::thread (std::move(tsk),arg).detach();
        return ret;
    }
    else return std::future<int>();
}


int main ()
{
    std::packaged_task<int(int)> tsk([](int x){return x*2;});


    std::future<int> fut = launcher(tsk,25);


    std::cout << "The double of 25 is " << fut.get() << ".\n";


    return 0;
}


std::packaged_task::get_future 介绍


返回一个与 packaged_task 对象共享状态相关的 future 对象。返回的 future 对象可以获得由另外一个线程在该 packaged_task 对象的共享状态上设置的某个值或者异常。


#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)> tsk([](int x) { return x * 3; })); // package task


    std::future<int> fut = tsk.get_future();   // 获取 future 对象.


    std::thread(std::move(tsk), 100).detach();   // 生成新线程并调用packaged_task.


    int value = fut.get();                     // 等待任务完成, 并获取结果.


    std::cout << "The triple of 100 is " << value << ".\n";


    return 0;

}


#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 doudou(int x) { return x * 2; }


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


std::future<int> fut = tsk.get_future();
//std::thread(std::move(tsk), 100).detach();
tsk(100);
std::cout << "The triple of 100 is " << fut.get() << ".\n";


// re-use same task object:
tsk.reset();
fut = tsk.get_future();
//std::thread(std::move(tsk), 200);
tsk(200);
std::cout << "Thre triple of 200 is " << fut.get() << ".\n";
std::packaged_task<int(int)> tsk2(doudou);
tsk.reset();
fut = tsk.get_future();
//tsk.swap(tsk2);
tsk(400);
std::cout << "the tsk inside tsk2 is doudou " << fut.get() << ".\n";
return 0;
}


std::future 介绍

前面已经多次提到过 std::future,那么 std::future 究竟是什么呢?简单地说,std::future 可以用来获取异步任务的结果,因此可以把它当成一种简单的线程间同步的手段。std::future 通常由某个 Provider 创建,你可以把 Provider 想象成一个异步任务的提供者,Provider 在某个线程中设置共享状态的值,与该共享状态相关联的 std::future 对象调用 get(通常在另外一个线程中) 获取该值,如果共享状态的标志不为 ready,则调用 std::future::get 会阻塞当前的调用者,直到 Provider 设置了共享状态的值(此时共享状态的标志变为 ready),std::future::get 返回异步任务的值或异常(如果发生了异常)

std::async 函数,本文后面会介绍 std::async() 函数。

std::promise::get_future,get_future 为 promise 类的成员函数。

一个 std::future 对象只有在有效(valid)的情况下才有用(useful),由 std::future 默认构造函数创建的 future 对象不是有效的(除非当前非有效的 future 对象被 move 赋值另一个有效的 future 对象)。

 在一个有效的 future 对象上调用 get 会阻塞当前的调用者,直到 Provider 设置了共享状态的值或异常(此时共享状态的标志变为 ready),std::future::get 将返回异步任务的值或异常(如果发生了异常)。


#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, 444444443);

    // 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();         // retrieve return value
//fut.get() will suspend the program until the is_prime return.

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

    return 0;
}

返回一个 std::shared_future 对象(本文后续内容将介绍 std::shared_future ),调用该函数之后,该 std::future 对象本身已经不和任何共享状态相关联,因此该 std::future 的状态不再是 valid 的了。
#include <iostream>       // std::cout
#include <future>         // std::async, std::future, std::shared_future
using namespace  std;
int do_get_value() { return 10; }


int main()
{
std::future<int> fut = std::async(do_get_value);
std::shared_future<int> shared_fut = fut.share();

// 共享的 future 对象可以被多次访问.
std::cout << "value: " << shared_fut.get() << '\n';
std::cout << "its double: " << shared_fut.get() * 2 << '\n';
if ( fut.valid())
{
cout << fut.get() * 5 << endl;
}
return 0;
}

std::future::valid()

检查当前的 std::future 对象是否有效,即释放与某个共享状态相关联。一个有效的 std::future 对象只能通过 std::async(), std::future::get_future 或者 std::packaged_task::get_future 来初始化。另外由 std::future 默认构造函数创建的 std::future 对象是无效(invalid)的,当然通过 std::future 的 move 赋值后该 std::future 对象也可以变为 valid。


#include <iostream>       // std::cout
#include <future>         // std::async, std::future
#include <utility>        // std::move

int do_get_value() { return 11; }

int main ()
{
    // 由默认构造函数创建的 std::future 对象,
    // 初始化时该 std::future 对象处于为 invalid 状态.
    std::future<int> foo, bar;
    foo = std::async(do_get_value); // move 赋值, foo 变为 valid.
    bar = std::move(foo); // move 赋值, bar 变为 valid, 而 move 赋值以后 foo 变为 invalid.

    if (foo.valid())
        std::cout << "foo's value: " << foo.get() << '\n';
    else
        std::cout << "foo is not valid\n";

    if (bar.valid())
        std::cout << "bar's value: " << bar.get() << '\n';
    else
        std::cout << "bar is not valid\n";

    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值