網站首頁 編程語言 正文
異步操作
C++11為異步操作提供了4個接口
- std::future : 異步指向某個任務,然后通過future特性去獲取任務函數的返回結果。
- std::aysnc: 異步運行某個任務函數。
- std::packaged_task :將任務和feature綁定在一起的模板,是一種封裝對任務的封裝。
- std::promise:承諾
std::future和std::aysnc 介紹
std::future期待一個函數的返回值,從一個異步調用的角度來說,future更像是執行函數的返回值,C++標準庫使用std::future為一次性事件建模,如果一個事件需要等待特定的一次性事件,那么這線程可以獲取個future對象來代表這個事件。
異步調用往往不知道何時返回,但是如果異步調用的過程需要同步,或者說后一個異步調用需要使用前一個異步調用的結果。這個時候就要用到future。也就是說,可選擇同步,也可選擇異步。
future的表現為期望,當前線程持有future時,期望從future獲取到想要的結果和返回,可以把future當做異步函數的返回值。
線程可以周期性的在這個future上等待一小段時間,檢查future是否已經ready,如果沒有,該線程可以先去做另一個任務,一旦future就緒,該future就無法復位(無法再次使用這個future等待這個事件),所以future代表的是一次性事件。
在庫的頭文件中聲明了兩種future,唯一future(std::future)和共享future(std::shared_future)這兩個是參照std::unique_ptr和std::shared_ptr設立的,前者的實例是僅有的一個指向其關聯事件的實例
,而后者可以有多個實例指向同一個關聯事件
,當事件就緒時,所有指向同一事件的std::shared_future實例會變成就緒。
跟thread類似,async允許你通過將額外的參數添加到調用中,來將附加參數傳遞給函數。
如果傳入的函數指針是某個類的成員函數,則還需要將類對象指針傳入(直接傳入,傳入指針,或者是std::ref封裝)。
默認情況下,std::async是否啟動一個新線程,或者在等待future時,任務是否同步運行都取決于你給的參數。這個參數為std::launch類型,async運行某個任務函數,至于異步運行還是同步運行,由這個參數決定
默認選項參數被設置為std::launch::any。如果函數被延遲運行可能永遠都不會運行,因為很有可能對應的future沒有調用get。
enum class launch{ async,deferred,sync=deferred,any=async|deferred };
- std::launch::async,表明函數會在創建的新線程上運行。
- std::launch::defered表明該函數會被延遲調用,直到在future上調用get()或者wait()為止。
- std::launch::sync = std::launch::defered,表明該函數會被延遲調用
- std::launch::any = std::launch::defered | std::launch::async,表明該函數會被延遲調用,調用時在新線程上運行。
std::future和std::aysnc的使用Demo
這里我們future了兩個函數,第一個函數設置為異步,那么在第20行之后,就會創建一個新線程并運行,而不必等待result.get()。第二個函數沒有設置參數,那么默認是延遲調用,只有在result2.get()時,才會創建一個新線程并運行。
#include <iostream> #include <future> #include <thread> using namespace std; int find_result_to_add() { //std::this_thread::sleep_for(std::chrono::seconds(2)); // 用來測試異步延遲的影響 std::cout << "find_result_to_add" << std::endl; return 1 + 1; } int find_result_to_add2(int a, int b) { //std::this_thread::sleep_for(std::chrono::seconds(5)); // 用來測試異步延遲的影響 return a + b; } void do_other_things() { std::cout << "do_other_things" << std::endl; std::this_thread::sleep_for(std::chrono::seconds(5)); } int main() { //async異步 std::future<int> result = std::async(std::launch::async,find_result_to_add); //std::future<decltype (find_result_to_add())> result = std::async(find_result_to_add); //auto result = std::async(find_result_to_add); // 推薦的寫法用aoto do_other_things(); std::cout << "result: " << result.get() << std::endl; // 延遲是否有影響? //std::future<decltype (find_result_to_add2(0, 0))> result2 = std::async(find_result_to_add2, 10, 20); //不寫默認any auto result2=std::async(find_result_to_add2, 10, 20); std::cout << "result2: " << result2.get() << std::endl; // 延遲是否有影響? std::cout << "main finish" << endl; return 0; }
std::packaged_task 介紹
如果說std::async和std::feature還是分開看的關系的話,那么std::packaged_task就是將任務和feature綁定在一起的模板,是一種封裝對任務的封裝。
The class template std::packaged_task wraps any Callable target (function, lambda expression, bind expression, or another function object) so that it can be invoked asynchronously. Its return value or exception thrown is stored in a shared state which can be accessed through std::future objects.
可以通過std::packaged_task對象獲取任務相關聯的feature,調用get_future()方法可以獲得std::packaged_task對象綁定的函數的返回值類型的future。std::packaged_task的模板參數是函數簽名。( 例如int add(int a, intb)的函數簽名就是int(int, int) )
std::packaged_task的使用Demo
#include <iostream> #include <future> using namespace std; int add(int a, int b, int c) { std::cout << "call add\n"; return a + b + c; } void do_other_things() { std::cout << "do_other_things" << std::endl; } int main() { std::packaged_task<int(int, int, int)> task(add); // 封裝任務 do_other_things(); std::future<int> result = task.get_future(); task(1, 1, 2); //必須要讓任務執行,否則在get()獲取future的值時會一直阻塞 std::cout << "result:" << result.get() << std::endl; return 0; }
std::promise 的介紹
std::promise提供了一種設置值
的方式,它可以在這之后通過相關聯
的std::future對象進行讀取。換種說法,之前已經說過std::future可以讀取一個異步函數的返回值了,那么這個std::promise就提供一種方式手動讓future就緒
出在promise創建好的時候future也已經創建好了,線程在創建promise的同時會獲得一個future,然后將promise傳遞給設置他的線程,當前線程則持有future,以便隨時檢查是否可以取值。
promise是一個承諾,當線程創建了promise對象后,這個promise對象向線程承諾他必定會被人設置一個值,和promise相關聯的future就是獲取其返回的手段。
std::promise的使用Demo
#include <future> #include <string> #include <thread> #include <iostream> using namespace std; void print(std::promise<std::string>& p) { p.set_value("There is the result whitch you want."); } void do_some_other_things() { std::cout << "Hello World" << std::endl; } int main() { std::promise<std::string> promise; std::future<std::string> result = promise.get_future(); std::thread th(print, std::ref(promise)); do_some_other_things(); std::cout << result.get() << std::endl; th.join(); return 0; }
function和bind
在設計回調函數的時候,無可避免地會接觸到可回調對象。在C++11中,提供了std::function和std::bind兩個方法來對可回調對象進行統一和封裝。(回調函數就是一個被作為參數傳遞的函數)
C++語言中有幾種可調用對象:函數、函數指針、lambda表達式、bind創建的對象以及重載了函數調用運算符的類。和其他對象一樣,可調用對象也有類型。例如,每個lambda有它自己唯一的(未命名)類類型;函數及函數指針的類型則由其返回值類型和實參類型決定。
function的用法
頭文件:#include <functional>
- 保存普通函數
//保存普通函數 void func1(int a) { cout << a << endl; } //1. 保存普通函數 std::function<void(int a)> func1_; func1_ = func1; func1_(2); //2
- 保存lambda表達式
//2. 保存lambda表達式 std::function<void()> func2_ = []() { cout << "hello lambda" << endl; }; func2_(); //hello world
- 保存成員函數
//保存成員函數 class A { public: A(string name) : name_(name) {} void func3(int i) const { cout <<name_ << ", " << i << endl; } private: string name_; }; //3 保存成員函數 std::function<void(const A&,int)> func3_ = &A::func3; A a("wxf"); func3_(a, 20);
完整代碼:
#include <iostream> #include <functional> using namespace std; //保存普通函數 void func1(int a) { cout << a << endl; } //保存成員函數 class A { public: A(string name) : name_(name) {} void func3(int i) const { cout <<name_ << ", " << i << endl; } private: string name_; }; int main() { cout << "main1 -----------------" << endl; //1. 保存普通函數 std::function<void(int a)> func1_; func1_ = func1; func1_(2); //2 cout << "\n\nmain2 -----------------" << endl; //2. 保存lambda表達式 std::function<void()> func2_ = []() { cout << "hello lambda" << endl; }; func2_(); //hello world cout << "\n\nmain3 -----------------" << endl; //3 保存成員函數 std::function<void(const A&,int)> func3_ = &A::func3; A a("wxf"); func3_(a, 20); return 0; }
main1 ----------------- 2 main2 ----------------- hello lambda main3 ----------------- wxf, 20
bind的用法
可將bind函數看作是一個通用的函數適配器
,它接受一個可調用對象,生成一個新的可調用對象來“適應”原對象的參數列表
。調用bind的一般形式:auto newCallable = bind(callable, arg_list);
其中,newCallable本身是一個可調用對象,arg_list是一個逗號分隔的參數列表,對應給定的callable的參數。即,當我們調用newCallable時,newCallable會調用callable,并傳給它arg_list中的參數。
arg_list中的參數可能包含形如placeholders::_n的名字,其中n是一個整數,這些參數是“占位符”,表示newCallable的參數,它們占據了傳遞給newCallable的參數的“位置”。數值n表示生成的可調用對象中參數的位置:placeholders::_1為newCallable的第一個參數,placeholders::_2為第二個參數,以此類推。
可能看描述還不是很懂,下面來看看代碼:
#include <iostream> #include <functional> using namespace std; void fun_1(int x,int y,int z) { cout<<"fun_1 print unchanged: x=" <<x<<",y="<< y << ",z=" <<z<<endl; } void fun_2(int &a,int &b) { a++; b++; cout<<"fun_2 print Increment: a=" <<a<<",b="<<b<<endl; } class A { public: // 重載fun_3,主要bind的時候需要 // std::bind((void(A::*)(int, int))&A::fun_3 void fun_3(int k,int m) { cout << "fun_3 a = " << a << cout <<"\t print unchanged: k="<<k<<",m="<<m<<endl; } // std::bind((void(A::*)(string))&A::fun_3 void fun_3(string str) { cout<<"fun_3 print: str="<<str<<endl; } int a; }; int main() { //f1的類型為 function<void(int, int, int)> cout << "\n\nstd::bind(fun_1, 1, 2, 3) -----------------\n"; auto f1 = std::bind(fun_1, 1, 2, 3); //表示綁定函數 fun 的第一,二,三個參數值為:1 2 3 f1(); //print: x=1,y=2,z=3 cout << "\n\nstd::bind(fun_1, 10, 20, 30) -----------------\n"; auto f1_1 = std::bind(fun_1, 10, 20, 30); //表示綁定函數 fun 的第一,二,三個參數值為: 1 2 3 f1_1(); cout << "\n\nstd::bind(fun_1, placeholders::_1,placeholders::_2, 3) -----------------\n"; auto f2 = std::bind(fun_1, placeholders::_1, placeholders::_2, 3); //表示綁定函數 fun_1的第三個參數為 3,而fun_1的第一,二個參數分別由調用 f2 的第一,二個參數指定 f2(1,2);//print: x=1,y=2,z=3 f2(10,21,30); // 傳入30也沒有用 cout << "\n\nstd::bind(fun_1,placeholders::_2,placeholders::_1,3) -----------------\n"; auto f3 = std::bind(fun_1,placeholders::_2,placeholders::_1,3); //表示綁定函數 fun_1 的第三個參數為 3,而fun_1的第一,二個參數分別由調用 f3 的第二,一個參數指定 //注意: f2 和 f3 的區別。 f3(1,2);//print: x=2,y=1,z=3 cout << "\n\nstd::bind(fun_2, placeholders::_1, n) -----------------\n"; int m = 2; int n = 3; ////表示綁定fun_2的第一個參數為n, fun_2的第二個參數由調用f4的第一個參數(_1)指定。 auto f4 = std::bind(fun_2, placeholders::_1, n); //func_2(3,<f4_1>) f4(m); //print: m=3,n=4 cout<<"m="<<m<<endl;//m=3 說明:bind對于不事先綁定的參數,通過std::placeholders傳遞的參數是通過引用傳遞的,如m cout<<"n="<<n<<endl;//n=3 說明:bind對于預先綁定的函數參數是通過值傳遞的,如n cout << "\n\nstd::bind(&A::fun_3,&a1,40,50) -----------------\n"; A a; a.a = 10; //f5的類型為 function<void(int, int)> auto f5 = std::bind((void(A::*)(int, int))A::fun_3, &a, 40, 50); f5(10,20);//參數以及寫死,傳參沒用 cout << "\n\nstd::bind(&A::fun_3, &a2,placeholders::_1,placeholders::_2) -----------------\n"; A a2; a2.a = 20; //f5的類型為 function<void(int, int)> auto f6 = std::bind((void(A::*)(int, int))&A::fun_3,&a2,placeholders::_1,placeholders::_2); //使用auto關鍵字 f6(10,20);//調用a.fun_3(10,20),print: k=10,m=20 cout << "\n\nstd::bind(&A::fun_3,a3,std::placeholders::_1,std::placeholders::_2) -----------------\n"; std::function<void(int,int)> fc = std::bind((void(A::*)(int,int))&A::fun_3, &a,std::placeholders::_1,std::placeholders::_2); fc(10,20); //調用a.fun_3(10,20) print: k=10,m=20 fc = std::bind((void(A::*)(int, int))&A::fun_3,&a2,std::placeholders::_1,std::placeholders::_2); cout << "\n\nstd::bind(&A::fun_3,&a1,std::placeholders::_1) -----------------\n"; auto f_str = std::bind((void(A::*)(string))&A::fun_3,a,std::placeholders::_1); f_str("wxf"); return 0; }
原文鏈接:https://juejin.cn/post/7138324152850579493
相關推薦
- 2022-01-30 h5 uniapp history模式下刷新頁面404
- 2022-02-02 Maven命令安裝本地jar包到本地倉庫
- 2022-07-28 docker容器間進行數據共享的三種實現方式_docker
- 2022-06-08 FreeRTOS實時操作系統多任務管理基礎知識_操作系統
- 2022-09-17 用C++來解決3*3拼圖的問題_C 語言
- 2023-01-23 Python列表對象中元素的刪除操作方法_python
- 2022-11-10 Netcore?Webapi返回數據的三種方式示例_C#教程
- 2022-03-14 Macos怎么快速打開終端
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支