C++11 中为了更好地支持多线程,引入了一些新的功能。
std::thread
std::thread作为一个类,其构造函数的作用是创建一个新线程并执行,构造函数有一个可变长的参数列表,第一个参数为一个 callable,后面的参数是该 callable 的入参。有三种 callable:
函数指针:
c++1
2void f() {}
std::thread t(f);Functor:
c++1
2
3
4class A {
void operator() {}
};
std::thread t(A());lambda 表达式:
c++1
2auto f = []() {};
std::thread t(f);
std::thread类有一个join方法,阻塞当前线程直到目标线程执行完成(别忘了调用join方法以回收线程)。
std::promise & std::future
std::promise和std::future是两个类模板,它们提供了一种线程之间同步数据的方法,即线程 a 等待线程 b set 了某个值以后才继续执行。一种常见的使用方法:
c++1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void f(std::promise<int> &&p) {
p.set_value(5);
}
int main() {
std::promise<int> p;
std::future<int> fu = p.get_future();
std::thread t(f, std::move(p));
std::cout << fu.get() << std::endl;
t.join();
return 0;
}promise和future成对使用,通过promise的get_future()方法可以获得其对应的future。当主线程调用fu.get()时会阻塞住,直到线程 t 中p.set_value()被执行。也就是说将要阻塞的线程要持有一个future对象(将来某个时刻该处的值会被设置),而负责设置该对象值的线程要持有一个promise对象(承诺会设置该处的值)。需要注意的是
promise的拷贝构造是被禁用掉的,所以创建线程 t 时不能写成std::thread t(f, p);,因为thread的构造函数经过一连串的函数调用后会在tuple文件的某处完美转发到一个构造promise的地方。所以如果创建线程时直接传入p的话会调用到promise的拷贝构造而报错。正确的做法是传入move(p),这样最终会调到promise的移动构造。(事实上future的拷贝构造也是被禁掉的)当然也可以传入指向
p的指针。当需要有多个
future对应一个promise时,可以使用std::shared_future,shared_future允许拷贝构造,也允许多次get。references:
std::async
std::async是一个函数模板,提供了一种异步执行某个函数的方法,第一个参数为 callable,之后的可变长参数列表为该 callable 的入参。(并非所有情况都会异步,和 launch policy 有关)std::async还有一个重载版本,第一个参数为 launch policy,第二个参数为 callable,之后为 callable 的入参。std::async的返回值类型是std::future,其模板参数类型为async入参 callable 的返回值类型。一种常见的使用方法:
c++1
2
3
4
5
6
7
8
9
10
11
12
int f() {
return 2;
}
int main() {
std::future<int> fu = std::async(f);
std::cout << fu.get() << std::endl;
return 0;
}launch policy 有两种:
async:保证异步行为,即创建一个新线程来执行 callable。deferred:在调用async返回的future的get()方法时,同步地执行 callable。
如果不指定 policy,那么采用
async还是deferred将取决于系统的负载,因为async异步、快,但是需要创建新线程,而deferred同步、慢,但是不需要创建新线程。references: