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: