1. 程式人生 > >C++11多執行緒(七):《 詳解三:std::future & std::shared_future》

C++11多執行緒(七):《 詳解三:std::future & std::shared_future》

#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:
**********@@@@@@@@@@
請按任意鍵繼續. . .