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,另外還會(huì)介紹 <future> 頭文件中的 std::async,std::future_category 函數(shù)以及相關(guān)枚舉類型。
std::future 介紹
前面已經(jīng)多次提到過(guò) std::future,那么 std::future 究竟是什么呢?簡(jiǎn)單地說(shuō),std::future 可以用來(lái)獲取異步任務(wù)的結(jié)果,因此可以把它當(dāng)成一種簡(jiǎn)單的線程間同步的手段。std::future 通常由某個(gè) Provider 創(chuàng)建,你可以把 Provider 想象成一個(gè)異步任務(wù)的提供者,Provider 在某個(gè)線程中設(shè)置共享狀態(tài)的值,與該共享狀態(tài)相關(guān)聯(lián)的 std::future 對(duì)象調(diào)用 get(通常在另外一個(gè)線程中) 獲取該值,如果共享狀態(tài)的標(biāo)志不為 ready,則調(diào)用 std::future::get 會(huì)阻塞當(dāng)前的調(diào)用者,直到 Provider 設(shè)置了共享狀態(tài)的值(此時(shí)共享狀態(tài)的標(biāo)志變?yōu)?ready),std::future::get 返回異步任務(wù)的值或異常(如果發(fā)生了異常)。
一個(gè)有效(valid)的 std::future 對(duì)象通常由以下三種 Provider 創(chuàng)建,并和某個(gè)共享狀態(tài)相關(guān)聯(lián)。Provider 可以是函數(shù)或者類,其實(shí)我們前面都已經(jīng)提到了,他們分別是:
- std::async 函數(shù),本文后面會(huì)介紹 std::async() 函數(shù)。
- std::promise::get_future,get_future 為 promise 類的成員函數(shù),詳見 C++11 并發(fā)指南四(<future> 詳解一 std::promise 介紹)。
- std::packaged_task::get_future,此時(shí) get_future為 packaged_task 的成員函數(shù),詳見C++11 并發(fā)指南四(<future> 詳解二 std::packaged_task 介紹)。
一個(gè) std::future 對(duì)象只有在有效(valid)的情況下才有用(useful),由 std::future 默認(rèn)構(gòu)造函數(shù)創(chuàng)建的 future 對(duì)象不是有效的(除非當(dāng)前非有效的 future 對(duì)象被 move 賦值另一個(gè)有效的 future 對(duì)象)。
?在一個(gè)有效的 future 對(duì)象上調(diào)用 get 會(huì)阻塞當(dāng)前的調(diào)用者,直到 Provider 設(shè)置了共享狀態(tài)的值或異常(此時(shí)共享狀態(tài)的標(biāo)志變?yōu)?ready),std::future::get 將返回異步任務(wù)的值或異常(如果發(fā)生了異常)。
下面以一個(gè)簡(jiǎn)單的例子說(shuō)明上面一段文字吧(參考):
// 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 成員函數(shù)
std::future 構(gòu)造函數(shù)
std::future 一般由 std::async, std::promise::get_future, std::packaged_task::get_future 創(chuàng)建,不過(guò)也提供了構(gòu)造函數(shù),如下表所示:
| future() noexcept; |
| future (const future&) = delete; |
| future (future&& x) noexcept; |
?
不過(guò) std::future 的拷貝構(gòu)造函數(shù)是被禁用的,只提供了默認(rèn)的構(gòu)造函數(shù)和 move 構(gòu)造函數(shù)(注:C++ 新特新)。另外,std::future 的普通賦值操作也被禁用,只提供了 move 賦值操作。如下代碼所示:
std::future<int> fut; // 默認(rèn)構(gòu)造函數(shù)fut = std::async(do_some_task); // move-賦值操作。std::future::share()
返回一個(gè) std::shared_future 對(duì)象(本文后續(xù)內(nèi)容將介紹 std::shared_future ),調(diào)用該函數(shù)之后,該 std::future 對(duì)象本身已經(jīng)不和任何共享狀態(tài)相關(guān)聯(lián),因此該 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 對(duì)象可以被多次訪問(wèn).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 |
當(dāng)與該 std::future 對(duì)象相關(guān)聯(lián)的共享狀態(tài)標(biāo)志變?yōu)?ready 后,調(diào)用該函數(shù)將返回保存在共享狀態(tài)中的值,如果共享狀態(tài)的標(biāo)志不為 ready,則調(diào)用該函數(shù)會(huì)阻塞當(dāng)前的調(diào)用者,而此后一旦共享狀態(tài)的標(biāo)志變?yōu)?ready,get 返回 Provider 所設(shè)置的共享狀態(tài)的值或者異常(如果拋出了異常)。
請(qǐng)看下面的程序:
#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()
檢查當(dāng)前的 std::future 對(duì)象是否有效,即釋放與某個(gè)共享狀態(tài)相關(guān)聯(lián)。一個(gè)有效的 std::future 對(duì)象只能通過(guò) std::async(), std::future::get_future 或者 std::packaged_task::get_future 來(lái)初始化。另外由 std::future 默認(rèn)構(gòu)造函數(shù)創(chuàng)建的 std::future 對(duì)象是無(wú)效(invalid)的,當(dāng)然通過(guò) std::future 的 move 賦值后該 std::future 對(duì)象也可以變?yōu)?valid。
#include <iostream> // std::cout #include <future> // std::async, std::future #include <utility> // std::moveint do_get_value() { return 11; }int main () {// 由默認(rèn)構(gòu)造函數(shù)創(chuàng)建的 std::future 對(duì)象,// 初始化時(shí)該 std::future 對(duì)象處于為 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()
等待與當(dāng)前std::future 對(duì)象相關(guān)聯(lián)的共享狀態(tài)的標(biāo)志變?yōu)?ready.
如果共享狀態(tài)的標(biāo)志不是 ready(此時(shí) Provider 沒(méi)有在共享狀態(tài)上設(shè)置值(或者異常)),調(diào)用該函數(shù)會(huì)被阻塞當(dāng)前線程,直到共享狀態(tài)的標(biāo)志變?yōu)?ready。
一旦共享狀態(tài)的標(biāo)志變?yōu)?ready,wait() 函數(shù)返回,當(dāng)前線程被解除阻塞,但是 wait() 并不讀取共享狀態(tài)的值或者異常。下面的代碼說(shuō)明了 std::future::wait() 的用法(參考)
執(zhí)行結(jié)果如下:
concurrency ) ./Future-wait Checking...194232491 is prime. concurrency )std::future::wait_for()
與 std::future::wait() 的功能類似,即等待與該 std::future 對(duì)象相關(guān)聯(lián)的共享狀態(tài)的標(biāo)志變?yōu)?ready,該函數(shù)原型如下:
template <class Rep, class Period>future_status wait_for (const chrono::duration<Rep,Period>& rel_time) const;而與 std::future::wait() 不同的是,wait_for() 可以設(shè)置一個(gè)時(shí)間段 rel_time,如果共享狀態(tài)的標(biāo)志在該時(shí)間段結(jié)束之前沒(méi)有被 Provider 設(shè)置為 ready,則調(diào)用 wait_for 的線程被阻塞,在等待了 rel_time 的時(shí)間長(zhǎng)度后 wait_until() 返回,返回值如下:
| future_status::ready | 共享狀態(tài)的標(biāo)志已經(jīng)變?yōu)?ready,即 Provider 在共享狀態(tài)上設(shè)置了值或者異常。 |
| future_status::timeout | 超時(shí),即在規(guī)定的時(shí)間內(nèi)共享狀態(tài)的標(biāo)志沒(méi)有變?yōu)?ready。 |
| future_status::deferred | 共享狀態(tài)包含一個(gè)?deferred 函數(shù)。 |
請(qǐng)看下面的例子:
#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) // 為了體現(xiàn)效果, 該函數(shù)故意沒(méi)有優(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); // 設(shè)置超時(shí)間隔.// 如果超時(shí),則輸出".",繼續(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 對(duì)象相關(guān)聯(lián)的共享狀態(tài)的標(biāo)志變?yōu)?ready,該函數(shù)原型如下:
template <class Rep, class Period>future_status wait_until (const chrono::time_point<Clock,Duration>& abs_time) const;而 與 std::future::wait() 不同的是,wait_until() 可以設(shè)置一個(gè)系統(tǒng)絕對(duì)時(shí)間點(diǎn) abs_time,如果共享狀態(tài)的標(biāo)志在該時(shí)間點(diǎn)到來(lái)之前沒(méi)有被 Provider 設(shè)置為 ready,則調(diào)用 wait_until 的線程被阻塞,在 abs_time 這一時(shí)刻到來(lái)之后 wait_for() 返回,返回值如下:
| future_status::ready | 共享狀態(tài)的標(biāo)志已經(jīng)變?yōu)?ready,即 Provider 在共享狀態(tài)上設(shè)置了值或者異常。 |
| future_status::timeout | 超時(shí),即在規(guī)定的時(shí)間內(nèi)共享狀態(tài)的標(biāo)志沒(méi)有變?yōu)?ready。 |
| future_status::deferred | 共享狀態(tài)包含一個(gè)?deferred 函數(shù)。 |
?
std::shared_future 介紹
std::shared_future 與 std::future 類似,但是 std::shared_future 可以拷貝、多個(gè) std::shared_future 可以共享某個(gè)共享狀態(tài)的最終結(jié)果(即共享狀態(tài)的某個(gè)值或者異常)。shared_future 可以通過(guò)某個(gè) std::future 對(duì)象隱式轉(zhuǎn)換(參見 std::shared_future 的構(gòu)造函數(shù)),或者通過(guò) std::future::share() 顯示轉(zhuǎn)換,無(wú)論哪種轉(zhuǎn)換,被轉(zhuǎn)換的那個(gè) std::future 對(duì)象都會(huì)變?yōu)?not-valid.
std::shared_future 構(gòu)造函數(shù)
std::shared_future 共有四種構(gòu)造函數(shù),如下表所示:
| 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) 即從一個(gè)有效的 std::future 對(duì)象構(gòu)造一個(gè) std::shared_future,構(gòu)造之后 std::future 對(duì)象 x 變?yōu)闊o(wú)效(not-valid)。
std::shared_future 其他成員函數(shù)
std::shared_future 的成員函數(shù)和 std::future 大部分相同,如下(每個(gè)成員函數(shù)都給出了連接):
operator=std::future_error 介紹
class future_error : public logic_error;std::future_error 繼承子 C++ 標(biāo)準(zhǔn)異常體系中的 logic_error,有關(guān) C++ 異常的繼承體系,請(qǐng)參考相關(guān)的C++教程 ;-)。
其他與 std::future 相關(guān)的函數(shù)介紹
與 std::future 相關(guān)的函數(shù)主要是 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 沒(méi)有指定異步任務(wù)(即執(zhí)行某一函數(shù))的啟動(dòng)策略(launch policy),而第二類函數(shù)指定了啟動(dòng)策略,詳見 std::launch 枚舉類型,指定啟動(dòng)策略的函數(shù)的 policy 參數(shù)可以是launch::async,launch::deferred,以及兩者的按位或( | )。
std::async() 的 fn 和 args 參數(shù)用來(lái)指定異步任務(wù)及其參數(shù)。另外,std::async() 返回一個(gè) std::future 對(duì)象,通過(guò)該對(duì)象可以獲取異步任務(wù)的值或異常(如果異步任務(wù)拋出了異常)。
下面介紹一下 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 相關(guān)的枚舉類介紹
下面介紹與 std::future 相關(guān)的枚舉類型。與 std::future 相關(guān)的枚舉類型包括:
enum class future_errc; enum class future_status; enum class launch;下面分別介紹以上三種枚舉類型:
std::future_errc 類型
std::future_errc 類型描述如下(參考):
| broken_promise | 0 | 與該 std::future 共享狀態(tài)相關(guān)聯(lián)的 std::promise 對(duì)象在設(shè)置值或者異常之前一被銷毀。 |
| future_already_retrieved | 1 | 與該 std::future 對(duì)象相關(guān)聯(lián)的共享狀態(tài)的值已經(jīng)被當(dāng)前 Provider 獲取了,即調(diào)用了 std::future::get 函數(shù)。 |
| promise_already_satisfied | 2 | std::promise 對(duì)象已經(jīng)對(duì)共享狀態(tài)設(shè)置了某一值或者異常。 |
| no_state | 3 | 無(wú)共享狀態(tài)。 |
std::future_status 類型(參考)
std::future_status 類型主要用在 std::future(或std::shared_future)中的 wait_for 和 wait_until 兩個(gè)函數(shù)中的。
| future_status::ready | 0 | wait_for(或wait_until) 因?yàn)?span style="font-size:14px">共享狀態(tài)的標(biāo)志變?yōu)?ready 而返回。 |
| future_status::timeout | 1 | 超時(shí),即 wait_for(或wait_until) 因?yàn)樵谥付ǖ臅r(shí)間段(或時(shí)刻)內(nèi)共享狀態(tài)的標(biāo)志依然沒(méi)有變?yōu)?ready而返回。 |
| future_status::deferred | 2 | 共享狀態(tài)包含了 deferred 函數(shù)。 |
std::launch 類型
該枚舉類型主要是在調(diào)用 std::async 設(shè)置異步任務(wù)的啟動(dòng)策略的。
| launch::async | Asynchronous: 異步任務(wù)會(huì)在另外一個(gè)線程中調(diào)用,并通過(guò)共享狀態(tài)返回異步任務(wù)的結(jié)果(一般是調(diào)用 std::future::get() 獲取異步任務(wù)的結(jié)果)。 |
| launch::deferred | Deferred: 異步任務(wù)將會(huì)在共享狀態(tài)被訪問(wèn)時(shí)調(diào)用,相當(dāng)與按需調(diào)用(即延遲(deferred)調(diào)用)。 |
請(qǐng)看下例(參考):
#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; }在我的機(jī)器上執(zhí)行結(jié)果:
with launch::async: *@**@**@**@**@*@@@@@with launch::deferred: **********@@@@@@@@@@總結(jié)
以上是生活随笔為你收集整理的C++11 并发指南四(future 详解三 std::future std::shared_future)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Echarts柱状图顶部加数量显示
- 下一篇: 谭浩强c++程序设计知识点思维导图