前面介紹了C++11的std::thread、std::mutex以及std::condition_variable,并實(shí)現(xiàn)了一個(gè)多線程通信的chan類,雖然由于篇幅的限制,該實(shí)現(xiàn)有些簡(jiǎn)陋云矫,甚至有些缺陷,但對(duì)于一般情況應(yīng)該還是夠用了汗菜。在C++11多線程系列的最后會(huì)獻(xiàn)上chan的最終版本让禀,敬請(qǐng)期待贵少。
本文將介紹C++11的另一大特性:異步運(yùn)行(std::async)。async顧名思義是將一個(gè)函數(shù)A移至另一線程中去運(yùn)行堆缘。A可以是靜態(tài)函數(shù)滔灶、全局函數(shù),甚至類成員函數(shù)吼肥。在異步運(yùn)行的過(guò)程中录平,如果A需要向調(diào)用者輸出結(jié)果怎么辦呢?std::async完美解決了這一問(wèn)題缀皱。在了解async的解決之道前斗这,我們需要一些知識(shí)儲(chǔ)備,那就是:std::promise啤斗、std::packaged_task和std::future表箭。異步運(yùn)行涉及的內(nèi)容較多,我們會(huì)分幾節(jié)來(lái)講钮莲。
1. std::promise
std::promise是一個(gè)模板類: template<typename R> class promise
免钻。其泛型參數(shù)R為std::promise對(duì)象保存的值的類型,R可以是void類型崔拥。std::promise保存的值可被與之關(guān)聯(lián)的std::future讀取极舔,讀取操作可以發(fā)生在其它線程。std::promise允許move語(yǔ)義(右值構(gòu)造链瓦,右值賦值)拆魏,但不允許拷貝(拷貝構(gòu)造、賦值)慈俯,std::future亦然渤刃。std::promise和std::future合作共同實(shí)現(xiàn)了多線程間通信。
1.1 設(shè)置std::promise的值
通過(guò)成員函數(shù)set_value可以設(shè)置std::promise中保存的值贴膘,該值最終會(huì)被與之關(guān)聯(lián)的std::future::get讀取到卖子。需要注意的是:set_value只能被調(diào)用一次,多次調(diào)用會(huì)拋出std::future_error異常步鉴。事實(shí)上std::promise::set_xxx函數(shù)會(huì)改變std::promise的狀態(tài)為ready揪胃,再次調(diào)用時(shí)發(fā)現(xiàn)狀態(tài)已要是reday了璃哟,則拋出異常氛琢。
#include <iostream> // std::cout, std::endl
#include <thread> // std::thread
#include <string> // std::string
#include <future> // std::promise, std::future
#include <chrono> // seconds
using namespace std::chrono;
void read(std::future<std::string> *future) {
// future會(huì)一直阻塞,直到有值到來(lái)
std::cout << future->get() << std::endl;
}
int main() {
// promise 相當(dāng)于生產(chǎn)者
std::promise<std::string> promise;
// future 相當(dāng)于消費(fèi)者, 右值構(gòu)造
std::future<std::string> future = promise.get_future();
// 另一線程中通過(guò)future來(lái)讀取promise的值
std::thread thread(read, &future);
// 讓read等一會(huì)兒:)
std::this_thread::sleep_for(seconds(1));
//
promise.set_value("hello future");
// 等待線程執(zhí)行完成
thread.join();
return 0;
}
// 控制臺(tái)輸: hello future
與std::promise關(guān)聯(lián)的std::future是通過(guò)std::promise::get_future獲取到的随闪,自己構(gòu)造出來(lái)的無(wú)效阳似。一個(gè)std::promise實(shí)例只能與一個(gè)std::future關(guān)聯(lián)共享狀態(tài),當(dāng)在同一個(gè)std::promise上反復(fù)調(diào)用get_future會(huì)拋出future_error異常铐伴。
共享狀態(tài)撮奏。在std::promise構(gòu)造時(shí)俏讹,std::promise對(duì)象會(huì)與共享狀態(tài)關(guān)聯(lián)起來(lái),這個(gè)共享狀態(tài)可以存儲(chǔ)一個(gè)R類型的值或者一個(gè)由std::exception派生出來(lái)的異常值畜吊。通過(guò)std::promise::get_future調(diào)用獲得的std::future與std::promise共享相同的共享狀態(tài)泽疆。
1.2 當(dāng)std::promise不設(shè)置值時(shí)
如果promise直到銷毀時(shí),都未設(shè)置過(guò)任何值玲献,則promise會(huì)在析構(gòu)時(shí)自動(dòng)設(shè)置為std::future_error殉疼,這會(huì)造成std::future.get拋出std::future_error異常。
#include <iostream> // std::cout, std::endl
#include <thread> // std::thread
#include <future> // std::promise, std::future
#include <chrono> // seconds
using namespace std::chrono;
void read(std::future<int> future) {
try {
future.get();
} catch(std::future_error &e) {
std::cerr << e.code() << "\n" << e.what() << std::endl;
}
}
int main() {
std::thread thread;
{
// 如果promise不設(shè)置任何值
// 則在promise析構(gòu)時(shí)會(huì)自動(dòng)設(shè)置為future_error
// 這會(huì)造成future.get拋出該異常
std::promise<int> promise;
thread = std::thread(read, promise.get_future());
}
thread.join();
return 0;
}
上面的程序在Clang下輸出:
future:4
The associated promise has been destructed prior to the associated state becoming ready.
1.3 通過(guò)std::promise讓std::future拋出異常
通過(guò)std::promise::set_exception函數(shù)可以設(shè)置自定義異常捌年,該異常最終會(huì)被傳遞到std::future瓢娜,并在其get函數(shù)中被拋出。
#include <iostream>
#include <future>
#include <thread>
#include <exception> // std::make_exception_ptr
#include <stdexcept> // std::logic_error
void catch_error(std::future<void> &future) {
try {
future.get();
} catch (std::logic_error &e) {
std::cerr << "logic_error: " << e.what() << std::endl;
}
}
int main() {
std::promise<void> promise;
std::future<void> future = promise.get_future();
std::thread thread(catch_error, std::ref(future));
// 自定義異常需要使用make_exception_ptr轉(zhuǎn)換一下
promise.set_exception(
std::make_exception_ptr(std::logic_error("caught")));
thread.join();
return 0;
}
// 輸出:logic_error: caught
std::promise雖然支持自定義異常礼预,但它并不直接接受異常對(duì)象:
// std::promise::set_exception函數(shù)原型
void set_exception(std::exception_ptr p);
自定義異趁呃可以通過(guò)位于頭文件exception下的std::make_exception_ptr函數(shù)轉(zhuǎn)化為std::exception_ptr。
1.4 std::promise<void>
通過(guò)上面的例子托酸,我們看到std::promise<void>
是合法的褒颈。此時(shí)std::promise.set_value不接受任何參數(shù),僅用于通知關(guān)聯(lián)的std::future.get()解除阻塞励堡。
1.5 std::promise所在線程退出時(shí)
std::async(異步運(yùn)行)時(shí)哈肖,開(kāi)發(fā)人員有時(shí)會(huì)對(duì)std::promise所在線程退出時(shí)間比較關(guān)注。std::promise支持定制線程退出時(shí)的行為:
- std::promise::set_value_at_thread_exit 線程退出時(shí)念秧,std::future收到通過(guò)該函數(shù)設(shè)置的值淤井。
- std::promise::set_exception_at_thread_exit 線程退出時(shí),std::future則拋出該函數(shù)指定的異常摊趾。
關(guān)于std::promise就是這些币狠,本文從使用角度介紹了std::promise的能力以及邊界,讀者如果想更深入了解該類砾层,可以直接閱讀一下源碼漩绵。
上一篇 C++11多線程-條件變量 |
目錄 | 下一篇 C++11多線程-packaged_task |
---|