C++11標準庫 未來體 <future> 梳理

来源:https://www.cnblogs.com/DSCL-ing/p/18302144
-Advertisement-
Play Games

目錄<future>future模板類成員函數:promise類promise的使用常式:packaged_task模板類常式:async模板函數常式:shared_future模板類 <future> 標準庫提供了一些工具來獲取非同步任務(即在單獨的線程中啟動的函數)的返回值,並捕捉其所拋出的異常。 ...


目錄

<future>

標準庫提供了一些工具來獲取非同步任務(即在單獨的線程中啟動的函數)的返回值,並捕捉其所拋出的異常。這些值在共用狀態中傳遞,其中非同步任務可以寫入其返回值或存儲異常,而且可以由持有該引用該共用態的 std::futurestd::shared_future 實例的線程檢驗、等待或是操作這個狀態。

併發支持庫 (C++11 起) - cppreference.com

future模板類

概念:

future - C++ Reference (cplusplus.com)

future類用於給線程提供一個方便管理和獲取非同步操作的結果的功能.

這個類允許將一個非同步操作封裝為一個未來可獲取結果的對象,從而實現了高效的併發編程。

註意:

  1. future本身沒有set方法,必須搭配promise進行使用.
  2. future對象要在需要傳遞結果的線程間可見(能夠看見同一個future) -- 引用傳遞

成員函數:

  • 定義
template< class T > class future;
template< class T > class future<T&>;
template<>          class future<void>; //空 -- 不需要返回結果的future.
  • 構造函數
future() noexcept; 						//空對象
future( future&& other ) noexcept;  	//只允許移動
future( const future& other ) = delete; //禁止拷貝
  • operator=
future& operator=( future&& other ) noexcept; 		//只允許移動
future& operator=( const future& other ) = delete;  //禁止拷貝
  • get

取出future對象內保存的數據.是阻塞函數,如果目標future所線上程一直沒有set_value,則會一直等待,直到set_value,成功獲取到數據後解除阻塞.

//三個重載函數,根據future的數據類型自動決定
T get();
T& get();
void get(); //空future對象沒有東西傳的時候使用
  • wait

阻塞等待,直到目標線程set_value.和get類似,只是沒有返回值.

void wait() const;
  • wait_for & wait_until

阻塞等待一段時長和等待到某時刻.之後會返回future的狀態

template< class Rep, class Period >
std::future_status wait_for( const std::chrono::duration<Rep,Period>& timeout_duration ) const;

template< class Clock, class Duration >
std::future_status wait_until( const std::chrono::time_point<Clock,Duration>& timeout_time ) const;
常量 解釋
future_status::deferred 子線程中的任務函仍未啟動(配合std::async生效)
future_status::ready 子線程中的任務已經執行完畢,結果已就緒
future_status::timeout 子線程中的任務正在執行中,指定等待時長已用完
  • share

返回多個線程共用的future對象,即shared_future模板類

shared_future<T> share();

返回值:shared_future<T>(std::move(*this))

promise類

promise類內封裝了future對象,用於管理和使用future對象.

  • 定義
template< class R > class promise;
template< class R > class promise<R&>;
template<>          class promise<void>;
  • 構造函數
promise();
promise( promise&& other ) noexcept; 	  //
promise( const promise& other ) = delete; //禁止拷貝
  • get_future

std::promise類內部管理著一個future類對象,調用get_future()就可以得到這個future對象了

std::future<T> get_future();
  • set_value系列
set_value 設置結果為指定值 (公開成員函數)
set_value_at_thread_exit 設置結果為指定值,同時僅線上程退出時分發提醒 (公開成員函數)
set_exception 設置結果為指示異常 (公開成員函數)
set_exception_at_thread_exit 設置結果為指示異常,同時僅線上程退出時分發提醒 (公開成員函數)
  1. set_value

存儲要傳出的 value 值,並立即讓狀態就緒,這樣數據被傳出其它線程就可以得到這個數據了。重載的第四個函數是為promise<void>類型的對象準備的。

void set_value( const R& value );
void set_value( R&& value );
void set_value( R& value );
void set_value();
  1. set_value_at_thread_exit

存儲要傳出的 value 值,但是不立即令狀態就緒。在當前線程退出時,子線程資源被銷毀,再令狀態就緒。

void set_value_at_thread_exit( const R& value );
void set_value_at_thread_exit( R&& value );
void set_value_at_thread_exit( R& value );
void set_value_at_thread_exit();

promise的使用常式:

#include <iostream>
#include <thread>
#include <functional>
#include <chrono>
#include <future>

void func(std::promise<std::string>& pro) {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    pro.set_value("hello world");
    std::this_thread::sleep_for(std::chrono::seconds(1));
}

int main() {

    std::promise<std::string> pro;
    std::thread t1(func, std::ref(pro));

    auto fut = pro.get_future();
    while (true) {
        bool flag = false;
        std::future_status ret = fut.wait_for(std::chrono::seconds(1));
        switch (ret) {
        case std::future_status::deferred:              //只有async函數才有效
            std::cout << "非同步線程還未啟動" << "\n";
            break;
        case std::future_status::ready:
            std::cout << "結果已就緒" << "\n";
            flag = true;
            break;
        case std::future_status::timeout:
            std::cout << "等待超時" << "\n";
            break;
        default:
            std::cout << "結果異常" << "\n";
        }
        if (flag == true) {
            break;
        }
        std::cout << "do something..." << "\n";
        std::this_thread::sleep_until(std::chrono::system_clock::now() + std::chrono::seconds(1));
    }
    std::cout << "輸出結果::" << fut.get() << "\n";

    t1.join();
    return 0;
}

packaged_task模板類

packaged_task包裝了一個可調用對象包裝器類對象,內部管理著一個future對象.

與promise用法類似.區別是promise只管理著future對象,而packaged_task還具備可調用對象的功能.

  • 定義
template< class > class packaged_task;
template< class R, class ...Args >
class packaged_task<R(Args...)>; 		//必須顯式指定模板參數
  • 構造函數
packaged_task() noexcept; //不可傳給子線程,那有什麼用?

template <class F>
explicit packaged_task( F&& f );

packaged_task( const packaged_task& ) = delete;  //禁止拷貝

packaged_task( packaged_task&& rhs ) noexcept;
  • get_future

和promise類似

std::future<R> get_future();

常式:

#include <iostream>
#include <thread>
#include <functional>
#include<chrono>
#include<future>
#include<string>

class Base {
public:
    using funcptr = std::string(*)(std::string, int num);

public:
    std::string operator()(std::string msg) {
        return std::string("operator(): " + msg);
    }
    operator funcptr() { //若類型轉換成函數,可以通過仿函數方式直接使用對象()進行調用;但如果與operator()的函數指針類型相同,則只會使用operator(),而不會調用operator T(),即operator T()被隱藏. 
        return Showmsg; //為了區分兩者,可使用不同的函數指針類型.
    }
    static std::string Showmsg(std::string msg, int num) {
        return std::string("showmsg(): " + msg + std::to_string(num));
    }
    std::string func(std::string msg) {
        return std::string("func(): " + msg);
    }
private:
};

int main() {
    std::cout << Base()("hello") << "\n";       //operator ()()
    std::cout << Base()("hello", 1) << "\n";   //operator T()()
    std::cout << Base() << "\n";                //operator T()

    Base b;
    std::packaged_task<std::string(std::string)> p_task1(b);
    std::packaged_task<std::string(std::string, int)> p_task2(b);
    std::packaged_task<std::string(std::string, int)> p_task3(std::bind(Base::Showmsg, "hello", 3));  //最好的用法,是讓bind自動推導模板參數 
    std::packaged_task<std::string(std::string)> p_task4(std::bind(&Base::func, &b,std::placeholders::_1));

    p_task1("p_tast1 hello");
    p_task2("p_tast2 hello", 2);
    p_task3("p_task3 hello", 3);
    p_task4("p_task4 hello");

    std::future<std::string> fut1 = p_task1.get_future();
    std::future<std::string> fut2 = p_task2.get_future();
    std::future<std::string> fut3 = p_task3.get_future();
    std::future<std::string> fut4 = p_task4.get_future();

    puts("");
    std::cout << fut1.get() << "\n";
    std::cout << fut2.get() << "\n";
    std::cout << fut3.get() << "\n";
    std::cout << fut4.get() << "\n";

    return 0;
}

image-20240712155251859

async模板函數

async是C++11提供用於簡化併發編程的模板函數,async函數同樣封裝有future對象,能夠將非同步任務的結果(return_value)保存在內部的future對象中,且該future對象為async的返回值,便於提取非同步任務的結果.

函數模板 std::async 非同步地運行函數 f,並返回最終將保有該函數調用結果的 std::future。

template< class Function, class... Args>
std::future<std::result_of_t<std::decay_t<Function>(std::decay_t<Args>...)>>
    async( Function&& f, Args&&... args );

template< class Function, class... Args >
std::future<std::result_of_t<std::decay_t<Function>(std::decay_t<Args>...)>>
    async( std::launch policy, Function&& f, Args&&... args );

參數:

f:可調用對象,這個對象在子線程中被作為任務函數使用

Args:傳遞給 f 的參數(實參)

policy:可調用對象·f的執行策略

策略 說明
std::launch::async 調用async函數時創建新的線程執行任務函數
std::launch::deferred 調用async函數時不執行任務函數,直到調用了future的get()或者wait()時才執行任務(這種方式不會創建新的線程)
std::launch::async|std::launch::deferred 自動選擇非同步或延遲執行任務,具體取決於系統負載和資源可用性等因素。

註:policy == std::launch::async|std::launch::deferred時,和預設不帶policy參數的async行為是完全一樣的,都是未定義的.

此時如果 policy 是 std::launch::async | std::launch::deferred 或者設置了額外位,那麼就會回退到推遲調用或其他由實現定義的策略。

std::async - cppreference.com

優點:

使用async()函數,是多線程操作中最簡單的一種方式,不需要自己創建線程對象,並且可以得到子線程函數的返回值。

常式:

#include <iostream>
#include <thread>
#include <functional>
#include<chrono>
#include<future>
#include<string>

int func(std::string msg,int x) {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout<<"thread_id: "<<std::this_thread::get_id() << "  msg: " << msg << "\n";
    return x; //返回給async中的future對象.
}

int main() {
    std::cout<<"main thread_id: "<<std::this_thread::get_id()<<"\n";
    std::future<int> fut1 = std::async(std::launch::async,func,"fut1",1);           //立即創建新一個線程執行
    std::future<int> fut2 = std::async(std::launch::deferred,func,"fut2",2);        //fut.get()調用後,在主線程執行
    auto fut3 = std::async(std::launch::async|std::launch::deferred,func,"fut3",3); //交由系統策略自動決定.
    
    std::cout<<fut1.get()<<"\n";
    std::cout<<fut2.get()<<"\n";
    std::cout<<fut3.get()<<"\n";

    return 0;
}

image-20240712200349884

shared_future模板類

引用:C++11中std::shared_future的使用_std shared future-CSDN博客

std::shared_future - cppreference.com

C++11中的std::shared_future是個模板類。與std::future類似,std::shared_future提供了一種訪問非同步操作結果的機制;

不同於std::future,std::shared_future允許多個線程等待同一個共用狀態;

不同於std::future僅支持移動操作,std::shared_future既支持移動操作也支持拷貝操作,而且多個shared_future對象可以引用相同的共用狀態。

std::shared_future還允許一旦共用狀態就緒就可以多次檢索共用狀態下的值(A shared_future object behaves like a future object, except that it can be copied, and that more than one shared_future can share ownership over their end of a shared state. They also allow the value in the shared state to be retrieved multiple times once ready)。

std::shared_future對象可以通過std::future對象隱式轉換,也可以通過顯示調用std::future::share顯示轉換,在這兩種情況下,原std::future對象都將變得無效。

共用狀態(shared state)的生存期至少要持續到與之關聯的最後一個對象被銷毀為止。與std::future不同,通過shared_future::get檢索的值不會釋放共用對象的所有權。

當你需要具有std::future的多個有效拷貝時會用到std::shared_future;或者多個使用者使用std::future時也會用到std::shared_future。

  1. 構造函數:

    (1).不帶參數的預設構造函數,此對象沒有共用狀態,因此它是無效的,但是它可以被賦予一個有效值;

    (2).拷貝構造:與const shared_future& x具有相同的共用狀態,並與之共用所有權;

    (3).支持移動構造。

  2. 析構函數:銷毀shared_future對象,它是異常安全的。如果對象有效(即它可以訪問共用狀態),則將其與對象接觸關聯;如果它是與共用狀態關聯的唯一對象,則共用對象本身也將被銷毀。

  3. get函數:

    (1).當共用狀態就緒時,返回存儲在共用狀態中的值的引用(或拋出異常)。

    (2).如果共用狀態尚未就緒(即provider提供者尚未設置其值或異常),則該函數將阻塞調用的線程直到就緒。

    (3).當共用狀態就緒後,則該函數將解除阻塞並返回(或拋出),但與future::get不同,不會釋放其共用狀態,允許其它shared_future對象也訪問存儲的值。

    (4).std::shared_future::get()不返回任何值,但仍等待共用狀態就緒才返回或拋出。

  4. operator=:

    (1).拷貝賦值:該對象與const shared_future& rhs關聯到相同的共用狀態,並與之共用所有權;

    (2).移動賦值:該對象獲取shared_future&& rhs的共用狀態,rhs不再有效。

  5. valid函數:檢查共用狀態的有效性,返回當前的shared_future對象是否與共用狀態關聯。

  6. wait函數:

    (1).等待共用狀態就緒。

    (2).如果共用狀態尚未就緒(即提供者尚未設置其值或異常),則該函數將阻塞調用的線程直到就緒。

    (3).當共用狀態就緒後,則該函數將解除阻塞並void返回。

  7. wait_for函數:

    (1).等待共用狀態在指定的時間內(time span)準備就緒。

    (2). 如果共用狀態尚未就緒(即提供者尚未設置其值或異常),則該函數將阻塞調用的線程直到就緒或已達到設置的時間。

    (3).此函數的返回值類型為枚舉類future_status。此枚舉類有三種label:ready:共用狀態已就緒;timeout:在指定的時間內未就緒;deferred:共用狀態包含了一個延遲函數(deferred function)。

    (4).如果共用狀態包含了一個延遲函數,則該函數不會阻塞,立即返回一個future_status::deferred值。

  8. wait_until函數:

    (1). 等待共用狀態在指定的時間點(time point)準備就緒。

    (2). 如果共用狀態尚未就緒(即提供者尚未設置其值或異常),則該函數將阻塞調用的線程直到就緒或已達到指定的時間點。

    (3).此函數的返回值類型為枚舉類future_status。

    (4).如果共用狀態包含了一個延遲函數,則該函數不會阻塞,立即返回一個future_status::deferred值。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 有的時候博客內容會有變動,首發博客是最新的,其他博客地址可能會未同步,認準https://blog.zysicyj.top 官方地址 Ubuntu 針對Docker客戶端版本大於 1.10.0 的用戶 您可以通過修改daemon配置文件/etc/docker/daemon.json來使用加速器 su ...
  • 在開發過程中,我們可能會遇到需要生成word,或者通過模板word替換相應內容的需求。但在文檔中插入圖片時,如果段落格式設置不對,就會導致圖片只顯示一點點或者不顯示。接下來就介紹一下java編輯word和插入圖片需怎麼處理。 1.引入依賴 首先我們在項目中引入Apache POI,用於讀取和操作wo ...
  • 1. Spring MVC 中的攔截器的使用“攔截器基本配置” 和 “攔截器高級配置” @目錄1. Spring MVC 中的攔截器的使用“攔截器基本配置” 和 “攔截器高級配置”2. 攔截器3. Spring MVC 中的攔截器的創建和基本配置3.1 定義攔截3.2 攔截器基本配置3.3 攔截器的 ...
  • 條形碼和二維碼是現代信息交換和數據存儲的重要工具,它們將信息以圖形的形式編碼,便於機器識別和數據處理,被廣泛應用於物流、零售、醫療、教育等各領域。本文將介紹如何使用Python快速生成各種常見的條形碼如Code 128、EAN-13,以及生成二維碼。 Python條碼庫 本文需要用到 Spire.B ...
  • 以下是一個詳細全面的 Spring Boot 使用 WebSocket 的知識點彙總 1. 配置 WebSocket 添加依賴 進入maven官網, 搜索spring-boot-starter-websocket,選擇版本, 然後把依賴複製到pom.xml的dependencies標簽中 配置 We ...
  • 引言 在現代的互聯網應用中,數據安全和隱私保護變得越來越重要。尤其是在介面返回數據時,如何有效地對敏感數據進行脫敏處理,是每個開發者都需要關註的問題。本文將通過一個簡單的Spring Boot項目,介紹如何實現介面數據脫敏。 一、介面數據脫敏概述 1.1 介面數據脫敏的定義 介面數據脫敏是指在介面返 ...
  • 本文介紹基於R語言中的UBL包,讀取.csv格式的Excel表格文件,實現SMOTE演算法與SMOGN演算法,對機器學習、深度學習回歸中,訓練數據集不平衡的情況加以解決的具體方法~ ...
  • 什麼是I8n 國際化(I18n)指的是設計和開發產品的過程,使得它們能夠適應多種語言和文化環境,而不需要進行大量的代碼更改。這通常涉及到創建一個基礎版本的產品,然後通過配置和資源文件來添加對不同語言和地區的支持。 這樣,當產品需要在新的地理區域或語言環境中使用時,只需要添加或更新相應的資源文件,而不 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...