日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++11 并发指南四(future 详解三 std::future std::shared_future)

發布時間:2025/3/15 c/c++ 24 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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::async 函數,本文后面會介紹 std::async() 函數。
  • std::promise::get_future,get_future 為 promise 類的成員函數,詳見 C++11 并發指南四(<future> 詳解一 std::promise 介紹)。
  • std::packaged_task::get_future,此時 get_future為 packaged_task 的成員函數,詳見C++11 并發指南四(<future> 詳解二 std::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; }

?std::future 成員函數

std::future 構造函數

std::future 一般由 std::async, std::promise::get_future, std::packaged_task::get_future 創建,不過也提供了構造函數,如下表所示:

default (1)copy [deleted] (2)move (3)
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 對象(本文后續內容將介紹 std::shared_future ),調用該函數之后,該 std::future 對象本身已經不和任何共享狀態相關聯,因此該 std::future 的狀態不再是 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 一共有三種形式,如下表所示(參考):

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

當與該 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_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 對象是否有效,即釋放與某個共享狀態相關聯。一個有效的 std::future 對象只能通過 std::async(), std::future::get_future 或者 std::packaged_task::get_future 來初始化。另外由 std::future 默認構造函數創建的 std::future 對象是無效(invalid)的,當然通過 std::future 的 move 賦值后該 std::future 對象也可以變為 valid。

#include <iostream> // std::cout #include <future> // std::async, std::future #include <utility> // std::moveint 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';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 對象相關聯的共享狀態的標志變為 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 returnsstd::cout << "is prime.\n";elsestd::cout << "is not prime.\n";return 0; }

執行結果如下:

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

請看下面的例子:

#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(1000); // 設置超時間隔.// 如果超時,則輸出".",繼續等待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 對象相關聯的共享狀態的標志變為 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)copy (2)move (3)move from future (4)
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 變為無效(not-valid)。

std::shared_future 其他成員函數

std::shared_future 的成員函數和 std::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 返回。)

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)specific policy (2)
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 沒有指定異步任務(即執行某一函數)的啟動策略(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_promise0與該 std::future 共享狀態相關聯的 std::promise 對象在設置值或者異常之前一被銷毀。
future_already_retrieved1與該 std::future 對象相關聯的共享狀態的值已經被當前 Provider 獲取了,即調用了 std::future::get 函數。
promise_already_satisfied2std::promise 對象已經對共享狀態設置了某一值或者異常。
no_state3無共享狀態。

std::future_status 類型(參考)

std::future_status 類型主要用在 std::future(或std::shared_future)中的 wait_for 和 wait_until 兩個函數中的。

類型取值
描述
future_status::ready0wait_for(或wait_until) 因為共享狀態的標志變為 ready 而返回。
future_status::timeout1超時,即 wait_for(或wait_until) 因為在指定的時間段(或時刻)內共享狀態的標志依然沒有變為 ready返回。
future_status::deferred2共享狀態包含了 deferred 函數。

std::launch 類型

該枚舉類型主要是在調用 std::async 設置異步任務的啟動策略的。

類型描述
launch::asyncAsynchronous: 異步任務會在另外一個線程中調用,并通過共享狀態返回異步任務的結果(一般是調用 std::future::get() 獲取異步任務的結果)。
launch::deferredDeferred: 異步任務將會在共享狀態被訪問時調用,相當與按需調用(即延遲(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; }

在我的機器上執行結果:

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

總結

以上是生活随笔為你收集整理的C++11 并发指南四(future 详解三 std::future std::shared_future)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。