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接收一個可調(diào)用對象(仿函數(shù)、lambda表達式、類成員函數(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 可以異步或是同步,取決于具體實現(xiàn)。
函數(shù)返回值:
函數(shù)返回值是std::future對象,我們可以執(zhí)行g(shù)et、wait、wait_for、wait_until函數(shù)獲取或者等待執(zhí)行結(jié)果。
調(diào)用std::future對象的get函數(shù)時,如果執(zhí)行的是異步執(zhí)行策略,如果異步執(zhí)行沒有結(jié)束,get函數(shù)調(diào)用會阻塞當前當前調(diào)用線程;如果執(zhí)行的是同步執(zhí)行策略,只有當調(diào)用get函數(shù)時才真正執(zhí)行。
調(diào)用std::future對象的wait*函數(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í)行超時;
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;
//獲取開始時間
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會一直阻塞當前線程
std::string dbData = resultFromDB.get();
//獲取結(jié)束時間
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ù)據(jù)讀取超時機制
有時我們不能無限制的等待異步任務執(zhí)行,可以設置超時等待時間(timeout),當超時時間到達時,可以選擇放棄等待異步任務。
如果代碼中,我們設置了1s的超時設置,用于檢查異步線程是否執(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;
//獲取開始時間
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é)束時間
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實現(xiàn)多線程并發(fā)
既然std::async可以實現(xiàn)異步調(diào)用,我們很容易就可以借用它實現(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、其它注意事項
在使用時需要注意,std::future對象的析構(gòu)需要等待std::async執(zhí)行完畢,也就是說,如下面的代碼并不能實現(xiàn)并發(fā)。原因在于std::async的返回的std::future對象無人接收,是個臨時變量,臨時變量的析構(gòu)會阻塞,直至std::async異步任務執(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-01

