C++11多線程-異步運(yùn)行(1)之std::promise

前面介紹了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
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市肛炮,隨后出現(xiàn)的幾起案子止吐,更是在濱河造成了極大的恐慌,老刑警劉巖侨糟,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件碍扔,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡秕重,警方通過(guò)查閱死者的電腦和手機(jī)不同,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人二拐,你說(shuō)我怎么就攤上這事服鹅。” “怎么了百新?”我有些...
    開(kāi)封第一講書(shū)人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵企软,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我饭望,道長(zhǎng)澜倦,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任杰妓,我火速辦了婚禮藻治,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘巷挥。我一直安慰自己桩卵,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布倍宾。 她就那樣靜靜地躺著雏节,像睡著了一般。 火紅的嫁衣襯著肌膚如雪高职。 梳的紋絲不亂的頭發(fā)上钩乍,一...
    開(kāi)封第一講書(shū)人閱讀 49,031評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音怔锌,去河邊找鬼寥粹。 笑死,一個(gè)胖子當(dāng)著我的面吹牛埃元,可吹牛的內(nèi)容都是我干的涝涤。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼岛杀,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼阔拳!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起类嗤,我...
    開(kāi)封第一講書(shū)人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤糊肠,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后遗锣,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體货裹,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年黄伊,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了泪酱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡还最,死狀恐怖墓阀,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情拓轻,我是刑警寧澤斯撮,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布,位于F島的核電站扶叉,受9級(jí)特大地震影響勿锅,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜枣氧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一溢十、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧达吞,春花似錦张弛、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至覆糟,卻和暖如春刻剥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背滩字。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工造虏, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人麦箍。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓酗电,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親内列。 傳聞我的和親對(duì)象是個(gè)殘疾皇子撵术,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • 接著上節(jié) condition_varible ,本節(jié)主要介紹future的內(nèi)容话瞧,練習(xí)代碼地址嫩与。本文參考http:/...
    jorion閱讀 14,760評(píng)論 1 5
  • 接著上節(jié) atomic,本節(jié)主要介紹condition_varible的內(nèi)容交排,練習(xí)代碼地址划滋。本文參考http://...
    jorion閱讀 8,464評(píng)論 0 7
  • 線程的產(chǎn)生 多線程并發(fā)高級(jí)接口std::async()和類std::future<> 1,async()使得可調(diào)用...
    龍遁流閱讀 972評(píng)論 0 1
  • 寫(xiě)項(xiàng)目的時(shí)候經(jīng)常會(huì)遇到按鈕上有圖片和文字的情況埃篓,每次圖片和按鈕的位置都會(huì)有些變化处坪,經(jīng)常要調(diào),沒(méi)有難點(diǎn),卻很麻煩同窘,所...
    黯魂粉玉閱讀 193評(píng)論 0 0
  • 在許多論壇里都看到過(guò)“什么東西吃了不會(huì)胖?”諸如此類的問(wèn)題丧没,一路看下來(lái)基本都是“西北風(fēng)”鹰椒、“奶嘴”,甚至“蛔蟲(chóng)”得...
    仙貝君閱讀 10,398評(píng)論 14 75