c++11多執行緒:std::future , std::promise和執行緒的返回值
std::future物件可以和asych,std::packaged_task,std::promise一起使用。這篇文章集中討論std::future和std::promise。
我們經常會遇到需要得到執行緒返回結果的情況,現在的問題是我們如何實現。
舉個例子:
假設在程式中,我們建立了一個壓縮給定資料夾的執行緒,並且我們希望該執行緒能夠返回新的zip檔案的名稱和大小。
有兩種實現方式:
1、老方法:使用指標線上程間共享資料
傳遞一個指標到新的執行緒中,該執行緒將在其中設定資料。直到主執行緒繼續等待使用條件變數。當新執行緒設定資料並通知條件變數時,主執行緒將喚醒並從該指標處獲取資料。
為了實現這一簡單功能,我們使用了一個條件變數、一個mutex鎖和一個指標,來實現捕獲返回值。
如果我們想要該執行緒在不同的時間點返回3個不同的值,問題會變得更加複雜,有沒有一種簡單的方法來從執行緒處獲取返回值呢?
答案是使用std::future
2、c++11的方法:使用std::future 和 std::promise
std::future是一個類模板(class template),其物件儲存未來的值,
到底什麼是未來的值呢?
事實上,一個std::future物件在內部儲存一個將來會被賦值的值,並提供了一個訪問該值的機制,通過get()成員函式實現。但如果有人檢視在get()函式可用之前通過它來訪問相關的值,那麼get()函式將會阻塞,直到該值可用。
std::promise也是一個類模板,其物件有可能在將來對值進行賦值,每個std::promise物件有一個對應的std::future物件,一旦由std::promise物件設定,std::future將會對其賦值。
std::promise物件與其管理的std::future物件共享資料。
逐步解析
線上程1中建立一個std::promise物件
std::promise<int> promiseObj;
目前為止,該promise物件沒有任何管理的值,但它承諾肯定會有人對其進行賦值,一旦被賦值,就可以通過其管理的std::future物件來獲取該值。
但是,假設執行緒1建立了該promise物件並將其傳給執行緒2,那麼執行緒1怎樣知道執行緒2什麼時候會對promise物件進行賦值呢?
答案是使用std::future物件
每個std::promise物件都有個對應的std::future物件,其他人可以通過它來獲取promise設定的值。
所以,執行緒1將會建立std::promise物件,然後在將其傳遞給執行緒2之前從它那裡獲取std::future物件。
std::future<int> futureObj = promiseObj.get_future();
現在,執行緒1將promiseObj傳遞給執行緒2.
那麼執行緒1將會獲取到執行緒2通過std::future的get函式設定在std::promise中的值,
int val = futureObj.get();
但是如果執行緒2還沒有對該值進行設定,那麼這個呼叫將會阻塞,直到執行緒2在promise物件中對該值進行設定。
promiseObj.set_value(45);
檢視下圖中的完整流程
看一個完整的std::future和std::promise的例子:
#include <iostream>
#include <thread>
#include <future>
void initiazer(std::promise<int>* promObj){
std::cout<<"Inside Thread"<<std::endl;
promObj->set_value(35);
}
int main(){
std::promise<int> promiseObj;
std::future<int> futureObj = promiseObj.get_future();
std::thread th(initiazer, &promiseObj);
std::cout<<futureObj.get()<<std::endl;
th.join();
return 0;
}
如果std::promise物件在賦值之前被銷燬,那麼管理的std::future物件上的get()呼叫將會丟擲異常。
除此之外,如果想要執行緒在不同時間點返回多個值,只需要線上程中傳輸多個std::promise物件,並從相關的多個std::futur物件中獲取多個返回值。