C++11——多執行緒程式設計10
翻譯來自:https://thispointer.com/c11-multithreading-part-10-packaged_task-example-and-tutorial/
這一節我們將討論 std::packaged_task
std::packaged_task<>
std::packaged_task<>是一個類模板,代表一個非同步任務。封裝了
1、可呼叫實體,即函式,lambda函式或函式物件
2、一個共享狀態,通過關聯的回撥來儲存返回的值或丟擲的異常。
假設我們有一個現有的函式從資料庫中提取資料並返回,
//從DB獲取資料 std::string gtDataFromDB(std::stringtoken){ //Do some stuff to fetch the data std::string data = "Data fetched from DB by filter :: + token; return data;
現在我們想在一個單獨的執行緒中執行該函式,但是我們如果在其他執行緒結束的情況下在主執行緒中獲取結果或異常返回?
一種方法是更改函式宣告並在函式中傳遞std::promise。在傳遞執行緒函式中的std::promise<>物件之前,將相關的std::future<>從中取出並儲存在主執行緒中。現在,線上程函式返回其值之前,應該在傳入的std::promise引數中設定它,所以它可以在主執行緒的相關std::future物件中使用。
但是,如果我們使用std::packaged_task<>,則可以避免建立std::promise和更改函式程式碼。
建立std::packaged_task<> 物件
std::packaged_task<>物件是一個類模板,因此我們需要將模板引數傳遞給packaged_task<>,即可呼叫函式的型別。
//建立封裝了回撥函式的packaged_task<> std::packaged_task<std::string(std::string)> task(getDataFromDB);
獲取future物件
//從packaged_task<>獲取相關future<>std::future<std::string> result = task.get_future();
傳遞packaged_task<>給執行緒
std::packaged_task<>可移動,但是不可複製,所以我們需要將它傳遞給執行緒
//傳遞packaged_task<>給執行緒以非同步執行 std::thread th(std::move(task), "Arg");
由於packaged_task只可以移動,不可以複製,因此我們在將它移動到執行緒之前從它那裡取出了 std::future<>物件。
執行緒將會執行這個任務,該任務在內部呼叫相關的可呼叫實體,例如我們的函式getDataFromDB()。
當這個函式給出返回值時,std::packaged_task<>將其設定為關聯的共享狀態,getDataFromDB()返回的結果或異常最終會在關聯的未來物件中可用。
//獲取packaged_task<>返回的結果,即getDataFromDB()返回的值。 std::string data = result.get();
get()函式將會阻塞呼叫執行緒,直到有可呼叫的實體返回,並且std::packaged_task<>將資料設定為其可共享的狀態
#include <iostream> #include <thread> #include <future> #include <string> //從DB獲取資料 std::string getDataFromDB(std::string token) { //獲取資料 std::string data = "Data fetched from DB by Filter :: " + token; return data; } int main() { //建立封裝回調函式的packaged_task<> std::packaged_task<std::string(std::string)> task(getDataFromDB); //從packaged_task<>獲取相關的future<> std::future<std::string> result = task.get_future(); //將packaged_task傳遞給執行緒以非同步執行 std::thread th(std::move(task), "Arg"); //join執行緒,阻塞直到執行緒完成時返回 th.join(); //獲取packaged_task<>的結果,即getDataFromDB()的返回值 std::string data = result.get(); std::cout << data << std::endl; return 0; }
輸出:
Data fetched from DB by Filter :: Arg
我們可以在同一行,用lambda函式或函式物件建立一個packaged_task
#include <iostream> #include <thread> #include <future> #include <string> int main() { //建立封裝了lambda函式的packaged_task<> std::packaged_task<std::string(std::string)> task([](std::string token) { std::string data = "Data From " + token; return data; }); //從packaged_task<>獲取相關的future<> std::future<std::string> result = task.get_future(); //將packaged_task傳遞給執行緒以非同步執行 std::thread th(std::move(task), "Arg"); //join執行緒,阻塞直到執行緒完成時返回 th.join(); //獲取packaged_task<>的結果,即getDataFromDB()的返回值 std::string data = result.get(); std::cout << data << std::endl; return 0; }
輸出:
Data fetched from DB by Filter :: Arg
使用函式物件建立packaged_task
#include <iostream> #include <thread> #include <future> #include <string> //從DB獲取資料的函式物件 struct DBDataFetcher { std::string operator ()(std::string token) { std::string data = "Data From " + token; return data; } }; int main() { //使用函式物件建立packaged_task std::packaged_task<std::string(std::string)> task(std::move(DBDataFetcher())); //從packaged_task<>獲取相關的future<> std::future<std::string> result = task.get_future(); //將packaged_task傳遞給執行緒以非同步執行 std::thread th(std::move(task), "Arg"); //join執行緒,阻塞直到執行緒完成時返回 th.join(); //獲取packaged_task<>的結果,即getDataFromDB()的返回值 std::string data = result.get(); std::cout << data << std::endl; return 0; }
輸出:
Data fetched from DB by Filter :: Arg