C++11 并发指南四(future 详解三 std::future std::shared_future)
上一講《C++11 并發(fā)指南四(<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 創(chuàng)建,你可以把 Provider 想象成一個異步任務的提供者,Provider 在某個線程中設置共享狀態(tài)的值,與該共享狀態(tài)相關聯的 std::future 對象調用 get(通常在另外一個線程中) 獲取該值,如果共享狀態(tài)的標志不為 ready,則調用 std::future::get 會阻塞當前的調用者,直到 Provider 設置了共享狀態(tài)的值(此時共享狀態(tài)的標志變?yōu)?ready),std::future::get 返回異步任務的值或異常(如果發(fā)生了異常)。
一個有效(valid)的 std::future 對象通常由以下三種 Provider 創(chuàng)建,并和某個共享狀態(tài)相關聯。Provider 可以是函數或者類,其實我們前面都已經提到了,他們分別是:
- std::async 函數,本文后面會介紹 std::async() 函數。
- std::promise::get_future,get_future 為 promise 類的成員函數,詳見 C++11 并發(fā)指南四(<future> 詳解一 std::promise 介紹)。
- std::packaged_task::get_future,此時 get_future為 packaged_task 的成員函數,詳見C++11 并發(fā)指南四(<future> 詳解二 std::packaged_task 介紹)。
一個 std::future 對象只有在有效(valid)的情況下才有用(useful),由 std::future 默認構造函數創(chuàng)建的 future 對象不是有效的(除非當前非有效的 future 對象被 move 賦值另一個有效的 future 對象)。
?在一個有效的 future 對象上調用 get 會阻塞當前的調用者,直到 Provider 設置了共享狀態(tài)的值或異常(此時共享狀態(tài)的標志變?yōu)?ready),std::future::get 將返回異步任務的值或異常(如果發(fā)生了異常)。
下面以一個簡單的例子說明上面一段文字吧(參考):
// 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; }?std::future 成員函數
std::future 構造函數
std::future 一般由 std::async, std::promise::get_future, std::packaged_task::get_future 創(chuàng)建,不過也提供了構造函數,如下表所示:
| future() noexcept; |
| future (const future&) = delete; |
| 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 對象(本文后續(xù)內容將介紹 std::shared_future ),調用該函數之后,該 std::future 對象本身已經不和任何共享狀態(tài)相關聯,因此該 std::future 的狀態(tài)不再是 valid 的了。
#include <iostream> // std::cout #include <future> // std::async, std::future, std::shared_futureint 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; }std::future::get()
std::future::get 一共有三種形式,如下表所示(參考):
| T get(); |
| R& future<R&>::get(); // when T is a reference type (R&) |
| void future<void>::get(); // when T is void |
當與該 std::future 對象相關聯的共享狀態(tài)標志變?yōu)?ready 后,調用該函數將返回保存在共享狀態(tài)中的值,如果共享狀態(tài)的標志不為 ready,則調用該函數會阻塞當前的調用者,而此后一旦共享狀態(tài)的標志變?yōu)?ready,get 返回 Provider 所設置的共享狀態(tài)的值或者異常(如果拋出了異常)。
請看下面的程序:
#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_exceptionvoid get_int(std::promise<int>& prom) {int x;std::cout << "Please, enter an integer value: ";std::cin.exceptions (std::ios::failbit); // throw on failbittry {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; }std::future::valid()
檢查當前的 std::future 對象是否有效,即釋放與某個共享狀態(tài)相關聯。一個有效的 std::future 對象只能通過 std::async(), std::future::get_future 或者 std::packaged_task::get_future 來初始化。另外由 std::future 默認構造函數創(chuàng)建的 std::future 對象是無效(invalid)的,當然通過 std::future 的 move 賦值后該 std::future 對象也可以變?yōu)?valid。
#include <iostream> // std::cout #include <future> // std::async, std::future #include <utility> // std::moveint do_get_value() { return 11; }int main () {// 由默認構造函數創(chuàng)建的 std::future 對象,// 初始化時該 std::future 對象處于為 invalid 狀態(tài).std::future<int> foo, bar;foo = std::async(do_get_value); // move 賦值, foo 變?yōu)?valid.bar = std::move(foo); // move 賦值, bar 變?yōu)?valid, 而 move 賦值以后 foo 變?yōu)?invalid.if (foo.valid())std::cout << "foo's value: " << foo.get() << '\n';elsestd::cout << "foo is not valid\n";if (bar.valid())std::cout << "bar's value: " << bar.get() << '\n';elsestd::cout << "bar is not valid\n";return 0; }std::future::wait()
等待與當前std::future 對象相關聯的共享狀態(tài)的標志變?yōu)?ready.
如果共享狀態(tài)的標志不是 ready(此時 Provider 沒有在共享狀態(tài)上設置值(或者異常)),調用該函數會被阻塞當前線程,直到共享狀態(tài)的標志變?yōu)?ready。
一旦共享狀態(tài)的標志變?yōu)?ready,wait() 函數返回,當前線程被解除阻塞,但是 wait() 并不讀取共享狀態(tài)的值或者異常。下面的代碼說明了 std::future::wait() 的用法(參考)
執(zhí)行結果如下:
concurrency ) ./Future-wait Checking...194232491 is prime. concurrency )std::future::wait_for()
與 std::future::wait() 的功能類似,即等待與該 std::future 對象相關聯的共享狀態(tài)的標志變?yōu)?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,如果共享狀態(tài)的標志在該時間段結束之前沒有被 Provider 設置為 ready,則調用 wait_for 的線程被阻塞,在等待了 rel_time 的時間長度后 wait_until() 返回,返回值如下:
| future_status::ready | 共享狀態(tài)的標志已經變?yōu)?ready,即 Provider 在共享狀態(tài)上設置了值或者異常。 |
| future_status::timeout | 超時,即在規(guī)定的時間內共享狀態(tài)的標志沒有變?yōu)?ready。 |
| future_status::deferred | 共享狀態(tài)包含一個?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) // 為了體現效果, 該函數故意沒有優(yōu)化. {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(1000); // 設置超時間隔.// 如果超時,則輸出".",繼續(xù)等待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 returnsstd::cout << "is prime.\n";elsestd::cout << "is not prime.\n";return 0; }std::future::wait_until()
與 std::future::wait() 的功能類似,即等待與該 std::future 對象相關聯的共享狀態(tài)的標志變?yōu)?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,如果共享狀態(tài)的標志在該時間點到來之前沒有被 Provider 設置為 ready,則調用 wait_until 的線程被阻塞,在 abs_time 這一時刻到來之后 wait_for() 返回,返回值如下:
| future_status::ready | 共享狀態(tài)的標志已經變?yōu)?ready,即 Provider 在共享狀態(tài)上設置了值或者異常。 |
| future_status::timeout | 超時,即在規(guī)定的時間內共享狀態(tài)的標志沒有變?yōu)?ready。 |
| future_status::deferred | 共享狀態(tài)包含一個?deferred 函數。 |
?
std::shared_future 介紹
std::shared_future 與 std::future 類似,但是 std::shared_future 可以拷貝、多個 std::shared_future 可以共享某個共享狀態(tài)的最終結果(即共享狀態(tài)的某個值或者異常)。shared_future 可以通過某個 std::future 對象隱式轉換(參見 std::shared_future 的構造函數),或者通過 std::future::share() 顯示轉換,無論哪種轉換,被轉換的那個 std::future 對象都會變?yōu)?not-valid.
std::shared_future 構造函數
std::shared_future 共有四種構造函數,如下表所示:
| shared_future() noexcept; |
| shared_future (const shared_future& x); |
| shared_future (shared_future&& x) noexcept; |
| shared_future (future<T>&& x) noexcept; |
最后 move from future(4) 即從一個有效的 std::future 對象構造一個 std::shared_future,構造之后 std::future 對象 x 變?yōu)闊o效(not-valid)。
std::shared_future 其他成員函數
std::shared_future 的成員函數和 std::future 大部分相同,如下(每個成員函數都給出了連接):
operator=std::future_error 介紹
class future_error : public logic_error;std::future_error 繼承子 C++ 標準異常體系中的 logic_error,有關 C++ 異常的繼承體系,請參考相關的C++教程 ;-)。
其他與 std::future 相關的函數介紹
與 std::future 相關的函數主要是 std::async(),原型如下:
| template <class Fn, class... Args>future<typename result_of<Fn(Args...)>::type>async(Fn&& fn, Args&&... args); |
| template <class Fn, class... Args>future<typename result_of<Fn(Args...)>::type>async(launch policy, Fn&& fn, Args&&... args); |
上面兩組 std::async() 的不同之處是第一類 std::async 沒有指定異步任務(即執(zhí)行某一函數)的啟動策略(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 0while(f.wait_until(std::chrono::system_clock::now() + std::chrono::seconds(1))!= std::future_status::ready) {std::cout << "task is running...\n";} #elsewhile(f.wait_for(std::chrono::seconds(1))!= std::future_status::ready) {std::cout << "task is running...\n";} #endifstd::cout << f.get() << std::endl;return EXIT_SUCCESS; }?
其他與 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 共享狀態(tài)相關聯的 std::promise 對象在設置值或者異常之前一被銷毀。 |
| future_already_retrieved | 1 | 與該 std::future 對象相關聯的共享狀態(tài)的值已經被當前 Provider 獲取了,即調用了 std::future::get 函數。 |
| promise_already_satisfied | 2 | std::promise 對象已經對共享狀態(tài)設置了某一值或者異常。 |
| no_state | 3 | 無共享狀態(tài)。 |
std::future_status 類型(參考)
std::future_status 類型主要用在 std::future(或std::shared_future)中的 wait_for 和 wait_until 兩個函數中的。
| future_status::ready | 0 | wait_for(或wait_until) 因為共享狀態(tài)的標志變?yōu)?ready 而返回。 |
| future_status::timeout | 1 | 超時,即 wait_for(或wait_until) 因為在指定的時間段(或時刻)內共享狀態(tài)的標志依然沒有變?yōu)?ready而返回。 |
| future_status::deferred | 2 | 共享狀態(tài)包含了 deferred 函數。 |
std::launch 類型
該枚舉類型主要是在調用 std::async 設置異步任務的啟動策略的。
| launch::async | Asynchronous: 異步任務會在另外一個線程中調用,并通過共享狀態(tài)返回異步任務的結果(一般是調用 std::future::get() 獲取異步任務的結果)。 |
| launch::deferred | Deferred: 異步任務將會在共享狀態(tài)被訪問時調用,相當與按需調用(即延遲(deferred)調用)。 |
請看下例(參考):
#include <iostream> // std::cout #include <future> // std::async, std::future, std::launch #include <chrono> // std::chrono::milliseconds #include <thread> // std::this_thread::sleep_forvoid 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; }在我的機器上執(zhí)行結果:
with launch::async: *@**@**@**@**@*@@@@@with launch::deferred: **********@@@@@@@@@@總結
以上是生活随笔為你收集整理的C++11 并发指南四(future 详解三 std::future std::shared_future)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Echarts柱状图顶部加数量显示
- 下一篇: 3ds Max制作客厅场景实例教程