您的位置:首页 > 编程语言 > C语言/C++

C++11多线程(七):《<future> 详解三:std::future & std::shared_future》

2016-01-05 15:13 696 查看
参考连接:
http://www.cnblogs.com/haippy/p/3280643.html 不错的博客
http://www.cplusplus.com/reference/future/future/ cplusplus官网

目录

1.std::future 介绍

2.std::future 构造函数

3.std::future::share()

4.std::future::get()

5.std::future::valid()

6.std::future::wait()

7.std::future::wait_for()

8.std::future::wait_until()

9.std::shared_future 介绍

10.std::shared_future 构造函数

11.std::shared_future 其他成员函数

12.std::future_error 介绍

13.std::future 相关的函数:std::async()

14.其他与 std::future 相关的枚举类介绍

1.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 返回异步任务的值或异常(如果发生了异常)。
一个有效(valid)的 std::future 对象通常由以下三种 Provider 创建,并和某个共享状态相关联。Provider 可以是函数或者类,其实我们前面都已经提到了,他们分别是:

a.std::async 函数,本文后面会介绍 std::async() 函数;

b.std::promise::get_future,get_future 为 promise 类的成员函数;

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

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

下面以一个简单的例子说明上面一段文字吧(参考):

// future example

#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

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

    return 0;

}

2.std::future 构造函数
std::future 一般由 std::async, std::promise::get_future, std::packaged_task::get_future 创建,不过也提供了构造函数。

不过 std::future 的拷贝构造函数是被禁用的,只提供了默认的构造函数和 move 构造函数(注:C++ 新特新)。另外,std::future 的普通赋值操作也被禁用,只提供了 move 赋值操作。如下代码所示:

  std::future<int> fut;           // 默认构造函数

  fut = std::async(do_some_task);   // move-赋值操作。

3.std::future::share()

返回一个 std::shared_future 对象(本文后续内容将介绍 std::shared_future ),调用该函数之后,该 std::future 对象本身已经不和任何共享状态相关联,因此该 std::future 的状态不再是 valid 的了。

示例代码:

#include <iostream>       // std::cout

#include <future>         // std::async, std::future, std::shared_future

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';

    return 0;

}

4.std::future::get()
std::future::get 一共有三种形式。
当与该 std::future 对象相关联的共享状态标志变为 ready 后,调用该函数将返回保存在共享状态中的值,如果共享状态的标志不为 ready,则调用该函数会阻塞当前的调用者,而此后一旦共享状态的标志变为 ready,get 返回 Provider 所设置的共享状态的值或者异常(如果抛出了异常)。

请看下面的程序:

#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(get_int, std::ref(prom));

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

    th1.join();

    th2.join();

    return 0;

}

5.std::future::valid()
检查当前的 std::future 对象是否有效,即释放与某个共享状态相关联。一个有效的 std::future 对象只能通过 std::async(), std::future::get_future 或者 std::packaged_task::get_future 来初始
4000
化。另外由 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;

}

6.std::future::wait()
等待与当前std::future 对象相关联的共享状态的标志变为 ready。
如果共享状态的标志不是 ready(此时 Provider 没有在共享状态上设置值(或者异常)),调用该函数会被阻塞当前线程,直到共享状态的标志变为 ready。

一旦共享状态的标志变为 ready,wait() 函数返回,当前线程被解除阻塞,但是 wait() 并不读取共享状态的值或者异常。下面的代码说明了 std::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 do_check_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(do_check_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;

}

7.std::future::wait_for()
std::future::wait() 的功能类似,即等待与该 std::future 对象相关联的共享状态的标志变为 ready,该函数原型如下:

template <class Rep, class Period>

  future_status wait_for (const chrono::duration<Rep,Period>& rel_time) const;

而与 std::future::wait() 不同的是,wait_for() 可以设置一个时间段 rel_time,如果共享状态的标志在该时间段结束之前没有被 Provider 设置为 ready,则调用 wait_for 的线程被阻塞,在等待了 rel_time 的时间长度后 wait_until() 返回,返回值如下:

返回值 描述

future_status::ready 共享状态的标志已经变为 ready,即 Provider 在共享状态上设置了值或者异常。

future_status::timeout 超时,即在规定的时间内共享状态的标志没有变为 ready。

future_status::deferred 共享状态包含一个 deferred 函数。

请看下面的例子:

#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 do_check_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(do_check_prime, 194232491);

std::cout << "Checking...\n";
std::chrono::milliseconds span(100); // 设置超时间隔.

// 如果超时,则输出".",继续等待
while (fut.wait_for(span) == std::future_status::timeout)
std::cout << '.';

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;

}

8.std::future::wait_until()
与 std::future::wait() 的功能类似,即等待与该 std::future 对象相关联的共享状态的标志变为 ready,该函数原型如下:

template <class Rep, class Period>

  future_status wait_until (const chrono::time_point<Clock,Duration>& abs_time) const;

而 与 std::future::wait() 不同的是,wait_until() 可以设置一个系统绝对时间点 abs_time,如果共享状态的标志在该时间点到来之前没有被 Provider 设置为 ready,则调用 wait_until 的线程被阻塞,在 abs_time 这一时刻到来之后 wait_for() 返回,返回值如下:

返回值 描述

future_status::ready 共享状态的标志已经变为 ready,即 Provider 在共享状态上设置了值或者异常。

future_status::timeout 超时,即在规定的时间内共享状态的标志没有变为 ready。

future_status::deferred 共享状态包含一个 deferred 函数。

9.std::shared_future 介绍
std::shared_future 与 std::future 类似,但是 std::shared_future 可以拷贝、多个 std::shared_future 可以共享某个共享状态的最终结果(即共享状态的某个值或者异常)。shared_future 可以通过某个 std::future 对象隐式转换(参见 std::shared_future 的构造函数),或者通过 std::future::share() 显示转换,无论哪种转换,被转换的那个
std::future 对象都会变为 not-valid.

10.std::shared_future 构造函数
std::shared_future 共有四种构造函数,如下所示:

default (1)

shared_future() noexcept;

copy (2)

shared_future (const shared_future& x);

move (3)

shared_future (shared_future&& x) noexcept;

move from future (4)

shared_future (future<T>&& x) noexcept;

最后 move from future(4) 即从一个有效的 std::future 对象构造一个 std::shared_future,构造之后 std::future 对象 x 变为无效(not-valid)。

11.std::shared_future 其他成员函数
std::shared_future 的成员函数和 std::future 大部分相同,如下:

(方法详细参考链接:http://www.cplusplus.com/reference/future/shared_future/)

operator=

赋值操作符,与 std::future 的赋值操作不同,std::shared_future 除了支持 move 赋值操作外,还支持普通的赋值操作。

get

获取与该 std::shared_future 对象相关联的共享状态的值(或者异常)。

valid

有效性检查。

wait

等待与该 std::shared_future 对象相关联的共享状态的标志变为 ready。

wait_for

等待与该 std::shared_future 对象相关联的共享状态的标志变为 ready。(等待一段时间,超过该时间段wait_for 返回。)

wait_until

等待与该 std::shared_future 对象相关联的共享状态的标志变为 ready。(在某一时刻前等待,超过该时刻 wait_until 返回。)

12.std::future_error 介绍
class future_error : public logic_error;
std::future_error 继承子 C++ 标准异常体系中的 logic_error,有关 C++ 异常的继承体系,请参考相关的C++教程 ;-)。

13.std::future 相关的函数:std::async()
与 std::future 相关的函数主要是 std::async(),原型如下:

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);

上面两组 std::async() 的不同之处是第一类 std::async 没有指定异步任务(即执行某一函数)的启动策略(launch policy),而第二类函数指定了启动策略,详见 std::launch 枚举类型,指定启动策略的函数的 policy 参数可以是launch::async,launch::deferred,以及两者的按位或( | )。

std::async() 的 fn 和 args 参数用来指定异步任务及其参数。另外,std::async() 返回一个 std::future 对象,通过该对象可以获取异步任务的值或异常(如果异步任务抛出了异常)。

下面介绍一下 std::async 的用法。

#include <stdio.h>

#include <stdlib.h>

#include <cmath>

#include <chrono>

#include <future>

#include <iostream>

double ThreadTask(int n) {

    std::cout << std::this_thread::get_id()

        << " start computing..." << std::endl;

    double ret = 0;

    for (int i = 0; i <= n; i++) {

        ret += std::sin(i);

    }

    std::cout << std::this_thread::get_id()

        << " finished computing..." << std::endl;

    return ret;

}

int main(int argc, const char *argv[])

{

    std::future<double> f(std::async(std::launch::async, ThreadTask, 100000000));

#if 0

    while(f.wait_until(std::chrono::system_clock::now() + std::chrono::seconds(1))

            != std::future_status::ready) {

        std::cout << "task is running...\n";

    }

#else

    while(f.wait_for(std::chrono::seconds(1))

            != std::future_status::ready) {

        std::cout << "task is running...\n";

    }

#endif

    std::cout << f.get() << std::endl;

    return EXIT_SUCCESS;

}

14.其他与 std::future 相关的枚举类介绍

enum class future_errc;

enum class future_status;

enum class launch;

参考链接:http://www.cplusplus.com/reference/future/future_errc/

std::launch 类型示例代码:

#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

do_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, do_print_ten, '*', 100);
std::future < void >bar =
std::async(std::launch::async, do_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, do_print_ten, '*', 100);
bar = std::async(std::launch::deferred, do_print_ten, '@', 200);
// deferred "get" (perform the actual calls):
foo.get();
bar.get();
std::cout << '\n';

return 0;

}

本机输出:

with launch::async:

*@**@**@**@**@*@@@@@

with launch::deferred:

**********@@@@@@@@@@

请按任意键继续. . .
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: