thread、future、promise、packaged_task、async之間有什么關(guān)系?
掃描二維碼
隨時隨地手機(jī)看文章
并發(fā)編程一般指多線程編程,C++11之后關(guān)于多線程編程有幾個高級API:
-
std::thread
-
std::future
-
std::shared_future
-
std::promise
-
std::packaged_task
-
std::async
可能很多人都搞不清楚它們之前有什么聯(lián)系,可以直接看這張圖:
如果連它們是什么都不知道的朋友,可以先看看這個:https://mp.weixin.qq.com/s/rPjRTOTYK2SGr6WxgWI_Vg
從這張圖我們可以大體看出來:
-
packaged_task ≈ promise + function
-
async ≈ thread + packaged_task
-
通過promise的get_future()可拿到future
-
通過future的share()可拿到shared_future
promise和future是線程之間的同步通道,類似于條件變量的封裝,看它的使用:
#include
#include
#include
int main() {
std::promiseprom;
std::futuref = prom.get_future();
prom.set_value(true);
std::cout << f.get() << std::endl;
}
首先創(chuàng)建一個promise,通過promise可以拿到future,future有wait()和get()等方法,這種方法會阻塞當(dāng)前線程,直到future的源promise調(diào)用了set_value,future的wait()只有阻塞功能,而get()方法不僅有阻塞功能,還能拿到set_value()設(shè)置的值。我舉個多線程的示例:
#include
#include
#include
int main() {
std::promiseprom;
auto f = prom.get_future();
std::thread t(
[](std::promisep) {
std::this_thread::sleep_for(std::chrono::seconds(2));
p.set_value(100);
},
std::move(prom));
std::cout << f.get() << std::endl;
if (t.joinable()) t.join();
}
這段代碼執(zhí)行后會在兩秒后輸出100。這個結(jié)果就驗(yàn)證了上面啰嗦的promise的future的get()的阻塞和獲取結(jié)果的能力。
注意:一個promise的set_value()只能調(diào)用一次,如果調(diào)用多次,就會throw exception,如果外部沒catch exception,程序就會crash。
promise的阻塞功能還是蠻好用的,我在工程中就經(jīng)常用到它。
介紹完promise,再來看看packaged_task:
#include
#include
#include
int main() {
std::packaged_task task([](int a, int b) { return a + b; });
auto f = task.get_future();
std::thread t(std::move(task), 1, 2);
std::cout << f.get() << std::endl;
if (t.joinable()) t.join();
}
可以拿這段代碼和上面那段promise的代碼對比看看,可以得出結(jié)論:
packaged_task ≈ promise + function
promise只能set_value,不太好執(zhí)行復(fù)雜的邏輯,有執(zhí)行函數(shù)+阻塞的需求時,就可以考慮使用packaged_task。
可以思考一下,如果要你封裝一個packaged_task,你會怎么做?
再看async:
#include
#include
#include
int main() {
auto f = std::async(
std::launch::async, [](int a, int b) { return a + b; }, 1, 2);
std::cout << f.get() << std::endl;
}
這里可以看到,使用了async后,連thread都不需要創(chuàng)建了,這也就驗(yàn)證了上面圖中的結(jié)論:
async ≈ thread + packaged_task
這里請注意:async中的第一個參數(shù)我使用的是std::launch::async,只有當(dāng)參數(shù)為std::launch::async時,函數(shù)才會異步執(zhí)行。
參數(shù)還可以是std::launch::deferred,參數(shù)為這個時,函數(shù)不會異步執(zhí)行,只有當(dāng)對應(yīng)的future調(diào)用了get時,函數(shù)才會執(zhí)行,而且是在當(dāng)前線程執(zhí)行。
關(guān)于async有幾個坑,我之前寫過一篇文章,可以看這個:async的兩個坑
介紹完async,再介紹下shared_future。
普通的future有個特點(diǎn),它不能拷貝,只能移動,這就意味著只能有一個線程一個實(shí)例可以通過get()拿到對應(yīng)的結(jié)果。
如果想要多個線程多個實(shí)例拿到結(jié)果,就可以使用shared_future,那怎么拿到shared_future,可以通過普通future的shared()方法。
#include
#include
#include
int main() {
std::promiseprom;
auto fu = prom.get_future();
auto shared_fu = fu.share();
auto f1 = std::async(std::launch::async, [shared_fu]() { std::cout << shared_fu.get() << std::endl; });
auto f2 = std::async(std::launch::async, [shared_fu]() { std::cout << shared_fu.get() << std::endl; });
prom.set_value(102);
f1.get();
f2.get();
}
看到這里,大家應(yīng)該明白thread、future、promise、packaged_task、async之間的關(guān)系了吧。





