C++11中多線程編程-std::async的深入講解
前言
C++11中提供了異步線程接口std::async,std::async是異步編程的高級封裝,相對于直接使用std::thread,std::async的優(yōu)勢在于:
1、std::async會自動創(chuàng)建線程去調(diào)用線程函數(shù),相對于低層次的std::thread,使用起來非常方便;
2、std::async返回std::future對象,通過返回的std::future對象我們可以非常方便的獲取到線程函數(shù)的返回結(jié)果;
3、std::async提供了線程的創(chuàng)建策略,可以指定同步或者異步的方式去創(chuàng)建線程;
1、函數(shù)原型
C++ 11中提供如下函數(shù)原型:
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 );
其中,參數(shù)f接收一個(gè)可調(diào)用對象(仿函數(shù)、lambda表達(dá)式、類成員函數(shù)、普通函數(shù)……),用于異步或是同步執(zhí)行。
參數(shù)policy用于指定同步執(zhí)行或者異步執(zhí)行可調(diào)用對象,它的可選值有三種:
1)std::launch::async:異步執(zhí)行可調(diào)用對象;
2)std::launch::deferred:同步執(zhí)行可調(diào)用對象;
3)std::launch::async | std::launch::deferred 可以異步或是同步,取決于具體實(shí)現(xiàn)。
函數(shù)返回值:
函數(shù)返回值是std::future對象,我們可以執(zhí)行g(shù)et、wait、wait_for、wait_until函數(shù)獲取或者等待執(zhí)行結(jié)果。
調(diào)用std::future對象的get函數(shù)時(shí),如果執(zhí)行的是異步執(zhí)行策略,如果異步執(zhí)行沒有結(jié)束,get函數(shù)調(diào)用會阻塞當(dāng)前當(dāng)前調(diào)用線程;如果執(zhí)行的是同步執(zhí)行策略,只有當(dāng)調(diào)用get函數(shù)時(shí)才真正執(zhí)行。
調(diào)用std::future對象的wait*函數(shù)時(shí),可能返回三種狀態(tài):
1)std::future_status::deferred:可調(diào)用對象尚未開始執(zhí)行;
2)std::future_status::ready:可調(diào)用對象執(zhí)行完畢;
3)std::future_status::timeout:可調(diào)用對象執(zhí)行超時(shí);
2、頭文件
#include <future>
3、同步或異步讀取文件內(nèi)容
我們模擬異步從數(shù)據(jù)庫中讀取數(shù)據(jù)和同步方式從文件中讀取數(shù)據(jù),從其中可以看到std::async的使用方法。
#include <iostream> #include <string> #include <chrono> #include <thread> #include <future> using namespace std::chrono; std::string fetchDataFromDB(std::string recvData) { std::cout << "fetchDataFromDB start" << std::this_thread::get_id() << std::endl; std::this_thread::sleep_for(seconds(5)); return "DB_" + recvData; } std::string fetchDataFromFile(std::string recvData) { std::cout << "fetchDataFromFile start" << std::this_thread::get_id() << std::endl; std::this_thread::sleep_for(seconds(3)); return "File_" + recvData; } int main() { std::cout << "main start" << std::this_thread::get_id() << std::endl; //獲取開始時(shí)間 system_clock::time_point start = system_clock::now(); std::future<std::string> resultFromDB = std::async(std::launch::async, fetchDataFromDB, "Data"); //從文件獲取數(shù)據(jù) std::future<std::string> fileData = std::async(std::launch::deferred, fetchDataFromFile, "Data"); //調(diào)用get函數(shù)fetchDataFromFile才開始執(zhí)行 std::string FileData = fileData.get(); //如果fetchDataFromDB執(zhí)行沒有完成,get會一直阻塞當(dāng)前線程 std::string dbData = resultFromDB.get(); //獲取結(jié)束時(shí)間 auto end = system_clock::now(); auto diff = duration_cast<std::chrono::seconds>(end - start).count(); std::cout << "Total Time taken= " << diff << "Seconds" << std::endl; //組裝數(shù)據(jù) std::string data = dbData + " :: " + FileData; //輸出組裝的數(shù)據(jù) std::cout << "Data = " << data << std::endl; return 0; }
代碼輸出:
main start140677737994048
fetchDataFromFile start140677737994048
fetchDataFromDB start140677720131328
Total Time taken= 5Seconds
Data = DB_Data :: File_Data
4、設(shè)置異步數(shù)據(jù)讀取超時(shí)機(jī)制
有時(shí)我們不能無限制的等待異步任務(wù)執(zhí)行,可以設(shè)置超時(shí)等待時(shí)間(timeout),當(dāng)超時(shí)時(shí)間到達(dá)時(shí),可以選擇放棄等待異步任務(wù)。
如果代碼中,我們設(shè)置了1s的超時(shí)設(shè)置,用于檢查異步線程是否執(zhí)行完畢。
#include <iostream> #include <string> #include <chrono> #include <thread> #include <future> using namespace std::chrono; std::string fetchDataFromDB(std::string recvData) { std::cout << "fetchDataFromDB start" << std::this_thread::get_id() << std::endl; std::this_thread::sleep_for(seconds(5)); return "DB_" + recvData; } int main() { std::cout << "main start" << std::this_thread::get_id() << std::endl; //獲取開始時(shí)間 system_clock::time_point start = system_clock::now(); std::future<std::string> resultFromDB = std::async(std::launch::async, fetchDataFromDB, "Data"); std::future_status status; std::string dbData; do { status = resultFromDB.wait_for(std::chrono::seconds(1)); switch (status) { case std::future_status::ready: std::cout << "Ready..." << std::endl; //獲取結(jié)果 dbData = resultFromDB.get(); std::cout << dbData << std::endl; break; case std::future_status::timeout: std::cout << "timeout..." << std::endl; break; case std::future_status::deferred: std::cout << "deferred..." << std::endl; break; default: break; } } while (status != std::future_status::ready); //獲取結(jié)束時(shí)間 auto end = system_clock::now(); auto diff = duration_cast<std::chrono::seconds>(end - start).count(); std::cout << "Total Time taken= " << diff << "Seconds" << std::endl; return 0; }
程序輸出:
main start140406593357632
fetchDataFromDB start140406575482624
timeout...
timeout...
timeout...
timeout...
Ready...
DB_Data
Total Time taken= 5Seconds
5、使用std::async實(shí)現(xiàn)多線程并發(fā)
既然std::async可以實(shí)現(xiàn)異步調(diào)用,我們很容易就可以借用它實(shí)現(xiàn)多線程并發(fā)。
#include <iostream> #include <vector> #include <algorithm> #include <numeric> #include <future> #include <string> #include <mutex> #include <thread> template <typename RandomIt> int parallel_sum(RandomIt beg, RandomIt end) { std::cout << "thread id:" << std::this_thread::get_id() << std::endl; auto len = end - beg; if (len < 1000) return std::accumulate(beg, end, 0); RandomIt mid = beg + len/2; auto handle_me = std::async(std::launch::async, parallel_sum<RandomIt>, mid, end); auto handle_bm = std::async(std::launch::async, parallel_sum<RandomIt>, beg, mid); // int sum = parallel_sum(beg, mid); return handle_bm.get() + handle_me.get(); } int main() { std::vector<int> v(10000, 1); std::cout << "The sum is " << parallel_sum(v.begin(), v.end()) << std::endl; }
程序輸出如下:
The sum is thread id:140594794530624
thread id:140594776655616
thread id:140594768262912
thread id:140594759870208
thread id:140594672297728
thread id:140594680690432
thread id:140594663905024
thread id:140594655512320
thread id:140594647119616
thread id:140594638726912
thread id:140594269644544
thread id:140594630334208
thread id:140594278037248
thread id:140594252859136
thread id:140594261251840
thread id:140594252859136
thread id:140594236073728
thread id:140594252859136
thread id:140594261251840
thread id:140594630334208
thread id:140594244466432
thread id:140594252859136
thread id:140594227681024
thread id:140594261251840
thread id:140593875384064
thread id:140593850205952
thread id:140593858598656
thread id:140593866991360
thread id:140594647119616
thread id:140594269644544
thread id:140594672297728
10000
6、其它注意事項(xiàng)
在使用時(shí)需要注意,std::future對象的析構(gòu)需要等待std::async執(zhí)行完畢,也就是說,如下面的代碼并不能實(shí)現(xiàn)并發(fā)。原因在于std::async的返回的std::future對象無人接收,是個(gè)臨時(shí)變量,臨時(shí)變量的析構(gòu)會阻塞,直至std::async異步任務(wù)執(zhí)行完成。
std::async(std::launch::async, []{ f(); }); // temporary's dtor waits for f() std::async(std::launch::async, []{ g(); }); // does not start until f() completes
參考材料
https://en.cppreference.com/w/cpp/thread/async
www.dbjr.com.cn/article/198761.htm
總結(jié)
到此這篇關(guān)于C++11中多線程編程-std::async深入講解的文章就介紹到這了,更多相關(guān)C++11多線程編程-std::async內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C++ Boost PropertyTree解析INI文件詳解
Boost PropertyTree庫不僅可以解析JSON,XML格式,還可以直接解析INI格式文件。這篇文章就是為大家介紹一下如何通過Boost PropertyTree解析INI文件,需要的可以參考一下2022-01-01C++實(shí)現(xiàn)LeetCode(47.全排列之二)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(47.全排列之二),本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07