std::promise
是一個模板類:template
。其泛型參數ResultType
是std::promise
對象保存的值的類型,例如std::promise
,ResultType
可以是void類型。std::promise
類型模板提供設置異步結果的方法,這樣其他線程就可以通過std::future
實例來讀取該結果。std::promise
和std::future
合作共同實現了多線程間通信。
在構造std::promise對象時,該對象與新的共享狀態(shared state)關聯。通過調用std::promise
的get_future
函數,可以將該共享狀態與std::future
對象關聯。調用get_future
之后,兩個對象共享相同的共享狀態:std::promise對象是異步提供程序(asynchronous provider),應在某個時刻為共享狀態設置一個值。std::future
對象是個異步返回對象,可以檢索共享狀態的值,并在必要時等待其準備就緒。 需要注意的是:set_value
只能被調用一次,多次調用會拋出std::future_error
異常。事實上std::promise::set_xxx
函數會改變std::promise的狀態為ready,再次調用時發現狀態已要是reday了,則拋出異常。
std::promise
實例是可以MoveConstructible
(移動構造)和MoveAssignable
(移動賦值),但是不能CopyConstructible
(拷貝構造)和CopyAssignable
(拷貝賦值)。
類型定義
templateclass promise
{public:
promise();
promise(promise&&) noexcept;
~promise();
promise& operator=(promise&&) noexcept;
templatepromise(std::allocator_arg_t, Allocator const&);
promise(promise const&) = delete;
promise& operator=(promise const&) = delete;
void swap(promise& ) noexcept;
std::futureget_future();
void set_value(see description);
void set_exception(std::exception_ptr p);
};
復制代碼
默認構造函數通過訪問新的空共享狀態來構造一個std::promise
對象(The object is initialized with access to a new empty shared state)。并且使用ResultType
類型的相關異步結果來構造std::promise
實例,不過異步結果并未就緒。當沒有足夠內存為異步結果進行分配,那么將拋出std::bad_alloc
型異常。
構造一個std::promise
對象,使用提供的分配器來為相關異步結果分配內存。
templatepromise(std::allocator_arg_t, Allocator const& alloc);
復制代碼
移動構造函數promise(promise&& other) noexcept;
復制代碼
通過另一個已存在對象,構造一個std::promise
對象。將已存在對象中的共享狀態以及相關異步結果的所有權轉移到新創建的std::promise
對象當中。之后,other將無關聯異步結果。
#include // std::cout
#include // std::thread
#include // std::promise, std::future
#includestd::promiseprom;
void print_global_promise () {std::futurefut = prom.get_future();
int x = fut.get();
std::cout<< "value: "<< x<< '\n'; // value is: 20
}
int main ()
{std::thread th1(print_global_promise);
// 等待線程th1線跑獲調用prom.get_future()
std::this_thread::sleep_for(std::chrono::seconds(1));
// 將prom所有權將轉移到新創建的對象prom2上
std::promiseprom2(std::move(prom));
// 對prom2進行設值,在之前的prom的get也能獲取到,最終輸出20
prom2.set_value (20);
th1.join();
return 0;
}
復制代碼
移動賦值操作符promise& operator=(promise&& other) noexcept;
復制代碼
在兩個std::promise
實例中轉移異步結果的所有權。在other和*this
之間進行異步結果所有權的轉移。當*this
已經有關聯的異步結果,那么該異步結果的狀態將會為就緒態,且伴隨一個std::future_error
類型異常,錯誤碼為std::future_errc::broken_promise
。
將other中關聯的異步結果轉移到*this
當中后。other中將無關聯異步結果。返回*this
。
#include // std::cout
#include // std::thread
#include // std::promise, std::future
std::promiseprom;
void print_global_promise () {std::futurefut = prom.get_future();
int x = fut.get();
std::cout<< "value: "<< x<< '\n';
}
int main () {std::thread th1(print_global_promise);
prom.set_value(10);
th1.join();
prom = std::promise(); // prom 被move賦值為一個新的 promise 對象.
std::thread th2 (print_global_promise);
prom.set_value (20);
th2.join();
return 0;
}
復制代碼
swap成員函數將兩個std::promise
實例中的關聯異步結果進行交換。
void swap(promise& other);
復制代碼
交換other和*this
當中的關聯異步結果。當swap使用other時,other中的異步結果就會與*this
中關聯異步結果相交換。二者返回來亦然。
放棄(abandon)共享狀態并銷毀std::promise
對象。當*this
具有關聯的異步結果,并且結果中沒有存儲值或異常(從未調用set_xx函數),那么結果將會置為就緒,伴隨一個std::future_error
異常,錯誤碼為std::future_errc::broken_promise
。可以看下面單獨章節說明例子。
返回一個與promise對象的共享狀態關聯的std::future對象。
std::futureget_future();
復制代碼
首先,*this
要具有關聯異步結果。最后返回與*this
關聯異步結果關聯的std::future
實例。
當std::future
已經通過get_future()
獲取過了,第二次獲取將會拋出一個std::future_error
類型異常,伴隨的錯誤碼為std::future_errc::future_already_retrieved
(Only one future object can be retrieved for each promise shared state)。 調用此函數后,promise應在某個時候使其共享狀態準備就緒(通過設置值或異常),否則將在銷毀時自動準備就緒并包含一個std::future_error類型的異常。
#include // std::cout
#include // std::promise, std::future
int main () {std::promiseprom; // 生成一個 std::promise對象.
std::futurefut = prom.get_future(); // 和 future 關聯.
try {std::futurefut2 = prom.get_future(); // 多次與 future 關聯.
} catch (std::exception &e) {std::cout<< "exception: "<< e.what()<< '\n';
}
return 0;
}
復制代碼
輸出:
exception: std::future_error: Future already retrieved
復制代碼
set_value成員函數存儲一個值到與*this關聯的異步結果中。
void promise::set_value();
void promise::set_value(R& r);
void promise::set_value(R const& r);
void promise::set_value(R&& r);
復制代碼
首先,*this
要具有關聯異步結果。最后,當ResultType
不是void型,就存儲r到*this
相關的異步結果當中。*this
相關的異步結果的狀態為就緒,且將值存入。任意等待異步結果的阻塞線程將解除阻塞。
如果異步結果已經存有一個值或一個異常,那么再次調用該函數將拋出std::future_error
型異常,伴隨錯誤碼為std::future_errc::promise_already_satisfied
。
#include // std::cout
#include // std::ref
#include // std::thread
#include // std::promise, std::future
void print_int(std::future& fut) {int x = fut.get(); // 獲取共享狀態的值.
std::cout<< "value: "<< x<< '\n'; // 打印 value: 10.
}
int main () {std::promiseprom; // 生成一個 std::promise對象.
std::futurefut = prom.get_future(); // 和 future 關聯.
std::thread t(print_int, std::ref(fut)); // 將 future 交給另外一個線程t.
prom.set_value(10); // 設置共享狀態的值, 此處和線程t保持同步.
try {prom.set_value(20); // 多次set_value.
} catch (std::exception &e) {std::cout<< "exception: "<< e.what()<< '\n';
}
t.join();
return 0;
}
復制代碼
輸出:
value: 10
exception: std::future_error: Promise already satisfied
復制代碼
set_value_at_thread_exit 成員函數存儲一個值到與*this
關聯的異步結果中,標記異步結果為“已存儲值”,但未就緒,直到線程退出時,異步結果的狀態才會被設置為就緒 (Stores the exception pointer p in the shared state without making it ready immediately. Instead, it will be made ready automatically at thread exit, once all objects of thread storage duration have been destroyed)。
void promise::set_value_at_thread_exit();
void promise::set_value_at_thread_exit(R& r);
void promise::set_value_at_thread_exit(R const& r);
void promise::set_value_at_thread_exit(R&& r);
復制代碼
當異步結果已經存有一個值或一個異常,那么將拋出std::future_error
型異常,伴隨錯誤碼為std::future_errc::promise_already_satisfied
。
存儲一個異常到與*this
關聯的異步結果中。
void set_exception(std::exception_ptr e);
復制代碼
首先,*this
具有關聯異步結果。然后將e存儲到*this
相關的異步結果中。在存儲異常后,*this
相關的異步結果的狀態將置為就緒。任何等待異步結果的阻塞線程將解除阻塞。
當異步結果已經存有一個值或一個異常,那么將拋出std::future_error
型異常,伴隨錯誤碼為std::future_errc::promise_already_satisfied
。
#include#includeint main() {std::promisep;
std::futuref = p.get_future();
try {// 可能拋出的代碼
throw std::runtime_error("Example");
} catch(...) {try {// 存儲任何拋出的異常于 promise
p.set_exception(std::current_exception());
} catch(...) {} // set_exception() 亦可能拋出
}
// 存儲任何拋出的異常于 promise,自定義異常需要使用make_exception_ptr轉換一下
// p.set_exception(std::make_exception_ptr(std::runtime_error("Example")));
try {std::cout<< f.get();
} catch(const std::exception& e) {std::cout<< "Exception from the thread: "<< e.what()<< '\n';
}
}
復制代碼
輸出:
Exception from the thread: Example
復制代碼
set_exception_at_thread_exit 成員函數存儲一個異常到共享狀態中,而不立即使狀態就緒,直到當前線程退出,銷毀所有擁有線程局域存儲期的變量后,異步結果才被置為就緒。
void set_exception_at_thread_exit(std::exception_ptr e);
復制代碼
當*this
無共享狀態。伴隨錯誤碼為std::future_errc::no_state
。當異步結果已經存有一個值或一個異常,那么將拋出std::future_error
型異常,伴隨錯誤碼為std::future_errc::promise_already_satisfied
。
如果promise對象直到析構銷毀時,都沒有調用set_xx接口設置過任何值,則promise會在析構時自動設置為std::future_error
錯誤異常,這會造成std::future.get
立刻解除阻塞并拋出std::future_error
異常。
#include// std::cout, std::endl
#include// std::thread
#include// std::promise, std::future
#include// seconds
using namespace std::chrono;
void future_get(std::futurefuture) {try {future.get();
} catch(std::future_error &e) {std::cerr<< e.code()<< "\n"<< e.what()<< std::endl;
}
}
int main() {std::thread thread;
{// 如果promise不設置任何值
// 則在promise析構時會自動設置為future_error
// 這會造成future.get拋出該異常
std::promisepromise;
thread = std::thread(future_get, promise.get_future());
}
std::cout<< "promise destory here"<< std::endl;
thread.join();
return 0;
}
復制代碼
輸出:
future:4
std::future_error: Broken promise
promise destory here
復制代碼
存儲自定義異常通過std::promise::set_exception
函數可以設置自定義異常,該異常最終會被傳遞到std::future,并在其get函數中被拋出。
自定義異常可以通過位于頭文件exception
下的std::make_exception_ptr
函數轉化為std::exception_ptr
。
#include#include#include#include// std::make_exception_ptr
struct MyException : public std::exception {const char * what () const throw ()
{return "Test promise exception";
}
};
void catch_exception(std::future&future) {try {future.get();
} catch (MyException &e) {std::cout<< "MyException: "<< e.what()<< std::endl;
}
}
int main() {std::promisepromise;
std::futurefuture = promise.get_future();
std::thread thread(catch_exception, std::ref(future));
// 自定義異常需要使用make_exception_ptr轉換一下
promise.set_exception(std::make_exception_ptr(MyException()));
thread.join();
return 0;
}
復制代碼
輸出:
MyException: Test promise exception
復制代碼
std::promise
std::promise
是合法的,它是std::promise的特例化。此時std::promise.set_value
不接受任何參數,僅用于通知關聯的std::future.get()
解除阻塞,void 特化,僅用于交流無狀態事件。
#include // std::cout
#include // std::ref
#include // std::thread
#include // std::promise, std::future
void print_void(std::future& fut) {fut.get(); // 獲取共享狀態的值void.
std::cout<< "value: void"<< '\n'; // 打印 value: 10.
}
int main () {std::promiseprom; // 生成一個 std::promise對象.
std::futurefut = prom.get_future(); // 和 future 關聯.
std::thread t(print_void, std::ref(fut)); // 將 future 交給另外一個線程t.
prom.set_value(); // 設置共享狀態的值, 此處和線程t保持同步.
t.join();
return 0;
}
復制代碼
std::promise所在線程退出時std::promise支持定制線程退出時的行為:
std::promise::set_value_at_thread_exit
線程退出時,std::future收到通過該函數設置的值。std::promise::set_exception_at_thread_exit
線程退出時,std::future則拋出該函數指定的異常。
#include#include#include#includestd::time_t now() {auto t0 = std::chrono::system_clock::now();
std::time_t time_t_today = std::chrono::system_clock::to_time_t(t0);
return time_t_today; // seconds
}
int main() {using namespace std::chrono_literals;
std::promisep;
std::futuref = p.get_future();
std::thread([&p] { std::this_thread::sleep_for(1s);
p.set_value_at_thread_exit(9);
}).detach();
std::cout<< now()<< "->Waiting...\n"<< std::flush;
f.wait();
std::cout<< now()<< "->Done!\nResult is: "<< f.get()<< '\n';
}
復制代碼
輸出:
1647995534->Waiting...
1647995535->Done!
Result is: 9
復制代碼
從上面的輸出可以看到,waiting和Done之間是有間隔1s鐘的,也就是說set_value_at_thread_exit
確實需要等線程結束后才會設值get為就緒解除阻塞。
下面簡單介紹一下std::future_errc
類型,future_errc 類枚舉 : 是 future_error 類報告的所有錯誤提供符號名稱。future_errc枚舉類型的值可用于創建error_condition
對象,可以用于與future_error
異常返回的值進行比較。
名稱 | 值 | 示意 |
---|---|---|
broken_promise | 0 | 與其關聯的 std::promise 生命周期提前結束。 |
future_already_retrieved | 1 | 重復調用 get() 函數。 |
promise_already_satisfied | 2 | 與其關聯的 std::promise 重復 set。 |
no_state | 4 | 無共享狀態。 |
#include // std::cerr
#include // std::promise, std::future_error, std::future_errc
int main () {std::promiseprom;
try {prom.get_future();
prom.get_future(); // throws std::future_error with future_already_retrieved
}
catch (std::future_error& e) {if (e.code() == std::make_error_condition(std::future_errc::future_already_retrieved))
std::cerr<< "[future already retrieved]\n";
else std::cerr<< "[unknown exception]\n";
}
return 0;
}
復制代碼
關于std::promise
就是這些了,注意簡單介紹std::promise的能力以及如何使用,如果想更深入了解該類,建議直接閱讀一下源碼。
參考
https//www.apiref.com/cpp-zh/cpp/…
你是否還在尋找穩定的海外服務器提供商?創新互聯www.cdcxhl.cn海外機房具備T級流量清洗系統配攻擊溯源,準確流量調度確保服務器高可用性,企業級服務器適合批量采購,新人活動首月15元起,快前往官網查看詳情吧
當前題目:C++多線程:std::promise-創新互聯
URL網址:http://vcdvsql.cn/article0/ddghio.html
成都網站建設公司_創新互聯,為您提供網站策劃、小程序開發、網站改版、電子商務、全網營銷推廣、ChatGPT
聲明:本網站發布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創新互聯