zoukankan      html  css  js  c++  java
  • C++11 并发指南四(<future> 详解三 std::future & std::shared_future)(转)

    上一讲《C++11 并发指南四(<future> 详解二 std::packaged_task 介绍)》主要介绍了 <future> 头文件中的 std::packaged_task 类,本文主要介绍 std::future,std::shared_future 以及 std::future_error,另外还会介绍 <future> 头文件中的 std::async,std::future_category 函数以及相关枚举类型。

    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 可以是函数或者类,其实我们前面都已经提到了,他们分别是:

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

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

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

     1 // future example
     2 #include <iostream>             // std::cout
     3 #include <future>               // std::async, std::future
     4 #include <chrono>               // std::chrono::milliseconds
     5 
     6 // a non-optimized way of checking for prime numbers:
     7 bool
     8 is_prime(int x)
     9 {
    10     for (int i = 2; i < x; ++i)
    11         if (x % i == 0)
    12             return false;
    13     return true;
    14 }
    15 
    16 int
    17 main()
    18 {
    19     // call function asynchronously:
    20     std::future < bool > fut = std::async(is_prime, 444444443);
    21 
    22     // do something while waiting for function to set future:
    23     std::cout << "checking, please wait";
    24     std::chrono::milliseconds span(100);
    25     while (fut.wait_for(span) == std::future_status::timeout)
    26         std::cout << '.';
    27 
    28     bool x = fut.get();         // retrieve return value
    29 
    30     std::cout << "
    444444443 " << (x ? "is" : "is not") << " prime.
    ";
    31 
    32     return 0;
    33 }

     std::future 成员函数

    std::future 构造函数

    std::future 一般由 std::async, std::promise::get_future, std::packaged_task::get_future 创建,不过也提供了构造函数,如下表所示:

    default (1)
    future() noexcept;
    
    copy [deleted] (2)
    future (const future&) = delete;
    
    move (3)
    future (future&& x) noexcept;

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

     std::future<int> fut;           // 默认构造函数
      fut = std::async(do_some_task);   // move-赋值操作。

    std::future::share()

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

     1 #include <iostream>       // std::cout
     2 #include <future>         // std::async, std::future, std::shared_future
     3 
     4 int do_get_value() { return 10; }
     5 
     6 int main ()
     7 {
     8     std::future<int> fut = std::async(do_get_value);
     9     std::shared_future<int> shared_fut = fut.share();
    10 
    11     // 共享的 future 对象可以被多次访问.
    12     std::cout << "value: " << shared_fut.get() << '
    ';
    13     std::cout << "its double: " << shared_fut.get()*2 << '
    ';
    14 
    15     return 0;
    16 }

    std::future::get()

    std::future::get 一共有三种形式,如下表所示(参考):

    generic template (1)
    T get();
    
    reference specialization (2)
    R& future<R&>::get();       // when T is a reference type (R&)
    
    void specialization (3)
    void future<void>::get();   // when T is void

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

    请看下面的程序:

     1 #include <iostream>       // std::cin, std::cout, std::ios
     2 #include <functional>     // std::ref
     3 #include <thread>         // std::thread
     4 #include <future>         // std::promise, std::future
     5 #include <exception>      // std::exception, std::current_exception
     6 
     7 void get_int(std::promise<int>& prom) {
     8     int x;
     9     std::cout << "Please, enter an integer value: ";
    10     std::cin.exceptions (std::ios::failbit);   // throw on failbit
    11     try {
    12         std::cin >> x;                         // sets failbit if input is not int
    13         prom.set_value(x);
    14     } catch (std::exception&) {
    15         prom.set_exception(std::current_exception());
    16     }
    17 }
    18 
    19 void print_int(std::future<int>& fut) {
    20     try {
    21         int x = fut.get();
    22         std::cout << "value: " << x << '
    ';
    23     } catch (std::exception& e) {
    24         std::cout << "[exception caught: " << e.what() << "]
    ";
    25     }
    26 }
    27 
    28 int main ()
    29 {
    30     std::promise<int> prom;
    31     std::future<int> fut = prom.get_future();
    32 
    33     std::thread th1(get_int, std::ref(prom));
    34     std::thread th2(print_int, std::ref(fut));
    35 
    36     th1.join();
    37     th2.join();
    38     return 0;
    39 }

    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。

     1 #include <iostream>       // std::cout
     2 #include <future>         // std::async, std::future
     3 #include <utility>        // std::move
     4 
     5 int do_get_value() { return 11; }
     6 
     7 int main ()
     8 {
     9     // 由默认构造函数创建的 std::future 对象,
    10     // 初始化时该 std::future 对象处于为 invalid 状态.
    11     std::future<int> foo, bar;
    12     foo = std::async(do_get_value); // move 赋值, foo 变为 valid.
    13     bar = std::move(foo); // move 赋值, bar 变为 valid, 而 move 赋值以后 foo 变为 invalid.
    14 
    15     if (foo.valid())
    16         std::cout << "foo's value: " << foo.get() << '
    ';
    17     else
    18         std::cout << "foo is not valid
    ";
    19 
    20     if (bar.valid())
    21         std::cout << "bar's value: " << bar.get() << '
    ';
    22     else
    23         std::cout << "bar is not valid
    ";
    24 
    25     return 0;
    26 }

    std::future::wait()

    等待与当前std::future 对象相关联的共享状态的标志变为 ready.

    如果共享状态的标志不是 ready(此时 Provider 没有在共享状态上设置值(或者异常)),调用该函数会被阻塞当前线程,直到共享状态的标志变为 ready。
    一旦共享状态的标志变为 ready,wait() 函数返回,当前线程被解除阻塞,但是 wait() 并不读取共享状态的值或者异常。下面的代码说明了 std::future::wait() 的用法(参考

     1 #include <iostream>                // std::cout
     2 #include <future>                // std::async, std::future
     3 #include <chrono>                // std::chrono::milliseconds
     4 
     5 // a non-optimized way of checking for prime numbers:
     6 bool do_check_prime(int x) // 为了体现效果, 该函数故意没有优化.
     7 {
     8     for (int i = 2; i < x; ++i)
     9         if (x % i == 0)
    10             return false;
    11     return true;
    12 }
    13 
    14 int main()
    15 {
    16     // call function asynchronously:
    17     std::future < bool > fut = std::async(do_check_prime, 194232491);
    18 
    19     std::cout << "Checking...
    ";
    20     fut.wait();
    21 
    22     std::cout << "
    194232491 ";
    23     if (fut.get()) // guaranteed to be ready (and not block) after wait returns
    24         std::cout << "is prime.
    ";
    25     else
    26         std::cout << "is not prime.
    ";
    27 
    28     return 0;
    29 }

    执行结果如下:

    concurrency ) ./Future-wait 
    Checking...
    
    194232491 is prime.
    concurrency ) 

    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 函数。

    请看下面的例子:

     1 #include <iostream>                // std::cout
     2 #include <future>                // std::async, std::future
     3 #include <chrono>                // std::chrono::milliseconds
     4 
     5 // a non-optimized way of checking for prime numbers:
     6 bool do_check_prime(int x) // 为了体现效果, 该函数故意没有优化.
     7 {
     8     for (int i = 2; i < x; ++i)
     9         if (x % i == 0)
    10             return false;
    11     return true;
    12 }
    13 
    14 int main()
    15 {
    16     // call function asynchronously:
    17     std::future < bool > fut = std::async(do_check_prime, 194232491);
    18 
    19     std::cout << "Checking...
    ";
    20     std::chrono::milliseconds span(1000); // 设置超时间隔.
    21 
    22     // 如果超时,则输出".",继续等待
    23     while (fut.wait_for(span) == std::future_status::timeout)
    24         std::cout << '.';
    25 
    26     std::cout << "
    194232491 ";
    27     if (fut.get()) // guaranteed to be ready (and not block) after wait returns
    28         std::cout << "is prime.
    ";
    29     else
    30         std::cout << "is not prime.
    ";
    31 
    32     return 0;
    33 }

    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 函数。

     

    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.

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

    std::shared_future 其他成员函数

    std::shared_future 的成员函数和 std::future 大部分相同,如下(每个成员函数都给出了连接):

    std::future_error 介绍

    class future_error : public logic_error;

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

    其他与 std::future 相关的函数介绍

    与 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 的用法。

     1 #include <stdio.h>
     2 #include <stdlib.h>
     3 
     4 #include <cmath>
     5 #include <chrono>
     6 #include <future>
     7 #include <iostream>
     8 
     9 double ThreadTask(int n) {
    10     std::cout << std::this_thread::get_id()
    11         << " start computing..." << std::endl;
    12 
    13     double ret = 0;
    14     for (int i = 0; i <= n; i++) {
    15         ret += std::sin(i);
    16     }
    17 
    18     std::cout << std::this_thread::get_id()
    19         << " finished computing..." << std::endl;
    20     return ret;
    21 }
    22 
    23 int main(int argc, const char *argv[])
    24 {
    25     std::future<double> f(std::async(std::launch::async, ThreadTask, 100000000));
    26 
    27 #if 0
    28     while(f.wait_until(std::chrono::system_clock::now() + std::chrono::seconds(1))
    29             != std::future_status::ready) {
    30         std::cout << "task is running...
    ";
    31     }
    32 #else
    33     while(f.wait_for(std::chrono::seconds(1))
    34             != std::future_status::ready) {
    35         std::cout << "task is running...
    ";
    36     }
    37 #endif
    38 
    39     std::cout << f.get() << std::endl;
    40 
    41     return EXIT_SUCCESS;
    42 }

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

    下面介绍与 std::future 相关的枚举类型。与 std::future 相关的枚举类型包括:

    enum class future_errc;
    enum class future_status;
    enum class launch;

    下面分别介绍以上三种枚举类型:

    std::future_errc 类型

    std::future_errc 类型描述如下(参考):

    类型取值描述
    broken_promise 0 与该 std::future 共享状态相关联的 std::promise 对象在设置值或者异常之前一被销毁。
    future_already_retrieved 1 与该 std::future 对象相关联的共享状态的值已经被当前 Provider 获取了,即调用了 std::future::get 函数。
    promise_already_satisfied 2 std::promise 对象已经对共享状态设置了某一值或者异常。
    no_state 3 无共享状态。

    std::future_status 类型(参考

    std::future_status 类型主要用在 std::future(或std::shared_future)中的 wait_for 和 wait_until 两个函数中的。

    类型取值
    描述
    future_status::ready 0 wait_for(或wait_until) 因为共享状态的标志变为 ready 而返回。
    future_status::timeout 1 超时,即 wait_for(或wait_until) 因为在指定的时间段(或时刻)内共享状态的标志依然没有变为 ready 而返回。
    future_status::deferred 2 共享状态包含了 deferred 函数。

    std::launch 类型

    该枚举类型主要是在调用 std::async 设置异步任务的启动策略的。

    类型描述
    launch::async Asynchronous: 异步任务会在另外一个线程中调用,并通过共享状态返回异步任务的结果(一般是调用 std::future::get() 获取异步任务的结果)。
    launch::deferred Deferred: 异步任务将会在共享状态被访问时调用,相当与按需调用(即延迟(deferred)调用)。

    请看下例(参考):

     1 #include <iostream>                // std::cout
     2 #include <future>                // std::async, std::future, std::launch
     3 #include <chrono>                // std::chrono::milliseconds
     4 #include <thread>                // std::this_thread::sleep_for
     5 
     6 void
     7 do_print_ten(char c, int ms)
     8 {
     9     for (int i = 0; i < 10; ++i) {
    10         std::this_thread::sleep_for(std::chrono::milliseconds(ms));
    11         std::cout << c;
    12     }
    13 }
    14 
    15 int
    16 main()
    17 {
    18     std::cout << "with launch::async:
    ";
    19     std::future < void >foo =
    20         std::async(std::launch::async, do_print_ten, '*', 100);
    21     std::future < void >bar =
    22         std::async(std::launch::async, do_print_ten, '@', 200);
    23     // async "get" (wait for foo and bar to be ready):
    24     foo.get();
    25     bar.get();
    26     std::cout << "
    
    ";
    27 
    28     std::cout << "with launch::deferred:
    ";
    29     foo = std::async(std::launch::deferred, do_print_ten, '*', 100);
    30     bar = std::async(std::launch::deferred, do_print_ten, '@', 200);
    31     // deferred "get" (perform the actual calls):
    32     foo.get();
    33     bar.get();
    34     std::cout << '
    ';
    35 
    36     return 0;
    37 }

    在我的机器上执行结果:

    with launch::async:
    *@**@**@**@**@*@@@@@
    
    with launch::deferred:
    **********@@@@@@@@@@

    转自:http://www.cnblogs.com/haippy/p/3280643.html

  • 相关阅读:
    linux使用shell执行一个Python文件
    shell编程
    mysql之通过cmd连接远程数据库
    单词辨析
    安全测试
    Linux下使用crontab执行一个shell脚本
    重新梳理Linux系统中Python环境的问题
    selenium 定位元素不稳定怎么解决?
    爬虫数据分析的前景
    Linux下部署python selenium UI自动化测试
  • 原文地址:https://www.cnblogs.com/zl1991/p/6994011.html
Copyright © 2011-2022 走看看