目录
1. 发起线程——thread<>
1.1 用函数对象表示线程任务
1.2 lambda表达式
2. 向线程任务传递参数
2.1 传递的参数是引用时
2.2 传递的参数是智能指针时
3. 等待线程完成——join()
1. 发起线程——thread<>
发起一个线程的动作两步即可完成,构建thread对象,指明该线程对象要运行的任务。以最简单的任务,一个空函数为例。发起一个线程执行空函数的方法如下,注意理解thread t1是在构建一个名为t1的thread对象,之后t1就与由他发起的线程关联,能够管控线程几乎所有的细节。
void print(){cout << "do print function" << endl;}
thread t1(print);
理解了thread对象之后,下面在深入理解一下任务。刚刚的任务是最简单的一种,是一个普通函数,返回为空,也不接收参数。函数返回后,线程即终止。复杂一些的任务就不能用简单的函数表示了,可以用函数对象,函数指针,lambda表达式等。
1.1 用函数对象表示线程任务
// 定义一个函数对象类
class A {
public:// 重载 operator(),不接受输入参数void operator()() {std::cout << "default" << std::endl;}
};int main() {A a2;std::thread t2(a2); // 调用 a2.operator()()t2.join(); // 等待线程结束return 0;
}
上面的代码使用了一个函数对象类,这里也可以理解成任务类,复杂的任务都可以丢给这个类,但是必须要重载()符号,这样才能触发线程的启动机制thread t(a)。当然上面的事例比较简单,我们尝试复杂一点点,增加一个传入参数的功能,那样就再提供一个重载函数:
#include <iostream>
#include <thread>
using namespace std;// 定义一个函数对象类
class A {
public:// 重载 operator(),接受一个字符串参数void operator()(const string& name) {cout << name << endl;}// 重载 operator(),不接受输入参数void operator()() {cout << "default" << endl;}
};int main() {A a1; // 创建函数对象// 将函数对象和参数传递给线程thread t1(a1, "input"); // 调用 a1.operator()("input")t1.join(); // 等待线程结束A a2;thread t2(a2); // 调用 a2.operator()()t2.join(); // 等待线程结束return 0;
}
1.2 lambda表达式
lambda表达式本质上解决了给thread构造函数传递临时变量而不是具名变量的问题。或者说的简单一点,有时候我们并不想显示的给一段函数名称或者在线程这里给一个任务命名,那么就可以使用lambda表达式。事例如下:
int main() {string name = "input";thread t3([name]{cout << "name" << endl;});t3.join(); return 0;
}
2. 向线程任务传递参数
其实上面的代码中,我们已经在给线程中的任务(函数)传递参数了,但是目前为止我们并没有发现给线程上的函数传递参数有什么不同,而这恰恰是在线程上写代码的一个特殊之处,所以这里单独分一个标题来讲。核心思想就是记住:线程具有内部存储空间,参数会先被复制进来,当成临时变量以右值的形式传递给函数。
2.1 传递的参数是引用时
所以,和原先传参考虑的不同,如果一旦是传入引用,而线程将他们作为右值传递给函数,与函数预期的传入不符,编译就不能通过。所以需要显式的在传递时表达出来,方法就是通过std::ref()。事例如下:
void f(Data& data);
int main(){Data data;// thread t4(f, data);thread t4(f, ref(data));t4.join(); return 0;}
2.2 传递的参数是智能指针时
单纯是指针时还好,右值传递有点类似于* const p, 不改变指针本身即可,当然如果想要改动指针本身再另外考虑。如果是智能指针,特别是unique_ptr,就会产生一个问题。传递进线程的unique_ptr会以副本的形式被复制到线程的空间中,这与智能存在唯一一个unique_ptr指向对象冲突,这时候就需要用到move()来转移对象的归属权。代码如下。p会先进入线程存储空间,然后将a对象的归属权转移(内部其实发生了拷贝)给任务函数f。
void f(unique_ptr<a>); // 线程的任务函数
unique_ptr<a> p; // 待传入线程的参数
thread t5(f, move(p));
3. 等待线程完成——join()
待续。。。