1. 程式人生 > 程式設計 >C++11中多執行緒程式設計-std::async的深入講解

C++11中多執行緒程式設計-std::async的深入講解

前言

C++11中提供了非同步執行緒介面std::async,std::async是非同步程式設計的高階封裝,相對於直接使用std::thread,std::async的優勢在於:

1、std::async會自動建立執行緒去呼叫執行緒函式,相對於低層次的std::thread,使用起來非常方便;

2、std::async返回std::future物件,通過返回的std::future物件我們可以非常方便的獲取到執行緒函式的返回結果;

3、std::async提供了執行緒的建立策略,可以指定同步或者非同步的方式去建立執行緒;

1、函式原型

C++ 11中提供如下函式原型:

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接收一個可呼叫物件(仿函式、lambda表示式、類成員函式、普通函式……),用於非同步或是同步執行。

引數policy用於指定同步執行或者非同步執行可呼叫物件,它的可選值有三種:

1)std::launch::async:非同步執行可呼叫物件;

2)std::launch::deferred:同步執行可呼叫物件;

3)std::launch::async | std::launch::deferred 可以非同步或是同步,取決於具體實現。

函式返回值:

函式返回值是std::future物件,我們可以執行get、wait、wait_for、wait_until函式獲取或者等待執行結果。

呼叫std::future物件的get函式時,如果執行的是非同步執行策略,如果非同步執行沒有結束,get函式呼叫會阻塞當前當前呼叫執行緒;如果執行的是同步執行策略,只有當呼叫get函式時才真正執行。

呼叫std::future物件的wait*函式時,可能返回三種狀態:

1)std::future_status::deferred:可呼叫物件尚未開始執行;

2)std::future_status::ready:可呼叫物件執行完畢;

3)std::future_status::timeout:可呼叫物件執行超時;

2、標頭檔案

#include <future>

3、同步或非同步讀取檔案內容

我們模擬非同步從資料庫中讀取資料和同步方式從檔案中讀取資料,從其中可以看到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");
 
 //從檔案獲取資料
 std::future<std::string> fileData = std::async(std::launch::deferred,fetchDataFromFile,"Data");
 
 //呼叫get函式fetchDataFromFile才開始執行
 std::string FileData = fileData.get();
 //如果fetchDataFromDB執行沒有完成,get會一直阻塞當前執行緒
 std::string dbData = resultFromDB.get();
 
 //獲取結束時間
 auto end = system_clock::now();
 
 auto diff = duration_cast<std::chrono::seconds>(end - start).count();
 std::cout << "Total Time taken= " << diff << "Seconds" << std::endl;
 
 //組裝資料
 std::string data = dbData + " :: " + FileData;
 
 //輸出組裝的資料
 std::cout << "Data = " << data << std::endl;
 
 return 0;
}

程式碼輸出:

main start140677737994048
fetchDataFromFile start140677737994048
fetchDataFromDB start140677720131328
Total Time taken= 5Seconds
Data = DB_Data :: File_Data

4、設定非同步資料讀取超時機制

有時我們不能無限制的等待非同步任務執行,可以設定超時等待時間(timeout),當超時時間到達時,可以選擇放棄等待非同步任務。

如果程式碼中,我們設定了1s的超時設定,用於檢查非同步執行緒是否執行完畢。

#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,"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;
   //獲取結果
   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);
 
 
 //獲取結束時間
 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實現多執行緒併發

既然std::async可以實現非同步呼叫,我們很容易就可以借用它實現多執行緒併發。

#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,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物件的析構需要等待std::async執行完畢,也就是說,如下面的程式碼並不能實現併發。原因在於std::async的返回的std::future物件無人接收,是個臨時變數,臨時變數的析構會阻塞,直至std::async非同步任務執行完成。

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.jb51.net/article/198761.htm

總結

到此這篇關於C++11中多執行緒程式設計-std::async深入講解的文章就介紹到這了,更多相關C++11多執行緒程式設計-std::async內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!