Thread、Future、Promise、Packaged_task、Async之間有什么關(guān)系?
并發(fā)編程一般指多線程編程,C++11之后關(guān)于多線程編程有幾個(gè)高級API:
- std::thread
- std::future
- std::shared_future
- std::promise
- std::packaged_task
- std::async
可能很多人都搞不清楚它們之前有什么聯(lián)系,可以直接看這張圖:
從這張圖我們可以大體看出來:
- packaged_task ≈ promise + function
- async ≈ thread + packaged_task
- 通過promise的get_future()可拿到future
- 通過future的share()可拿到shared_future
promise和future是線程之間的同步通道,類似于條件變量的封裝,看它的使用:
#include <future>
#include <iostream>
#include <thread>
int main() {
std::promise<bool> prom;
std::future<bool> f = prom.get_future();
prom.set_value(true);
std::cout << f.get() << std::endl;
}
首先創(chuàng)建一個(gè)promise,通過promise可以拿到future,future有wait()和get()等方法,這種方法會阻塞當(dāng)前線程,直到future的源promise調(diào)用了set_value,future的wait()只有阻塞功能,而get()方法不僅有阻塞功能,還能拿到set_value()設(shè)置的值。我舉個(gè)多線程的示例:
#include <future>
#include <iostream>
#include <thread>
int main() {
std::promise<int> prom;
auto f = prom.get_future();
std::thread t(
[](std::promise<int> p) {
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。這個(gè)結(jié)果就驗(yàn)證了上面啰嗦的promise的future的get()的阻塞和獲取結(jié)果的能力。
注意:一個(gè)promise的set_value()只能調(diào)用一次,如果調(diào)用多次,就會throw exception,如果外部沒catch exception,程序就會crash。
promise的阻塞功能還是蠻好用的,我在工程中就經(jīng)常用到它。
介紹完promise,再來看看packaged_task:
#include <future>
#include <iostream>
#include <thread>
int main() {
std::packaged_task<int(int, int)> 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ù)+阻塞的需求時(shí),就可以考慮使用packaged_task。
可以思考一下,如果要你封裝一個(gè)packaged_task,你會怎么做?
再看async:
#include <future>
#include <iostream>
#include <thread>
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中的第一個(gè)參數(shù)我使用的是std::launch::async,只有當(dāng)參數(shù)為std::launch::async時(shí),函數(shù)才會異步執(zhí)行。
參數(shù)還可以是std::launch::deferred,參數(shù)為這個(gè)時(shí),函數(shù)不會異步執(zhí)行,只有當(dāng)對應(yīng)的future調(diào)用了get時(shí),函數(shù)才會執(zhí)行,而且是在當(dāng)前線程執(zhí)行。
關(guān)于async有幾個(gè)坑,我之前寫過一篇文章,可以看這個(gè):async的兩個(gè)坑。
介紹完async,再介紹下shared_future。
普通的future有個(gè)特點(diǎn),它不能拷貝,只能移動,這就意味著只能有一個(gè)線程一個(gè)實(shí)例可以通過get()拿到對應(yīng)的結(jié)果。
如果想要多個(gè)線程多個(gè)實(shí)例拿到結(jié)果,就可以使用shared_future,那怎么拿到shared_future,可以通過普通future的shared()方法。
#include <future>
#include <iostream>
#include <thread>
int main() {
std::promise<int> prom;
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)系了吧。