持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天点击查看活动详情
创建线程
包含头文件#include<thread>
用函数创建线程
线程入口函数是可以给多个线程使用的
/*线程入口函数*/
void threadBegin()
{
}
thread a(threadBegin);//创建一个线程对象 括号里面需要放一个可调用对象用来构造对象 这个函数就变成这个线程的执行起点
创建线程的函数传参问题
void mprint(const int &a,char* b)
{
cout << a << endl;
cout << b << endl;
}
int main()
{
int a = 0;
char b[] = "abcd";
thread mobj(mprint,a,b);//这种方式将参数传入
//mobj.detach();
mobj.join();
return 0;
}
虽然用的是const int &a也就是引用方式 实际上子线程内部的a和主线程中的a地址不一样 并不是真的引用了 实际上是值传递 解决方法用thread mobj(mprint,ref(a),b); 但是如果通过指针传递 子线程中的b和主线程的b是一个地址 所以这样是很危险的
类型转化出现的问题
因为直接传指针是不安全的 所以我们把这个指针存储的值转换成string会好点 就会变成值传递 但是还是会出现问题 因为并发的原因我们并不知道 char b[]是什么时候转换成string的 可能出现char b[]已经在主函数中被回收了 再进行string的类型转换 (这里没懂 如果会出现这个问题 那为什么int a可以实现传值 难道不会出现int a在主函数中被收回了 再值传递给子线程的a吗)
void mprint(const int &a,const string& b)
{
cout << a << endl;
cout << b << endl;
}
int main()
{
int a = 0;
char b[] = "abcd";
thread mobj(mprint,a,b);//正确方式是thread mobj(mprint,a,string(b)); 生成一个临时的string
//mobj.detach();
mobj.join();
return 0;
}
证明string(b)是可行的
class A {
public:
double x, y;
A()
{
cout << "构造函数" << endl;
}
A(int i) {//类型转换构造函数
cout << "类型转换构造函数" << endl;
x = i; y = 0;
}
A(const A& c) :x(c.x), y(c.y)
{
cout << "调用了拷贝构造函数" << endl;
}
~A()
{
cout << "析构了" << endl;
}
};
void start(const A& a)
{
cout << "进入子线程了" << endl;
}
int main()
{
int x = 1;
thread mobj(start, x);
mobj.detach();
return 0;
}
这里很明显的说明了 可能在主线程结束后再执行A a = x的类型转换
所以并不是完全执行完thread mobj(start,x)里面的所有构造 主线程才会继续执行 所以就会出现出现前面的char b[]已经在主函数中被回收了 再进行string的类型转换
当我们改成A(x)时
很明显 当我们临时创建一个A类对象时 是一定先执行的
原因是什么呢?我觉得应该是A(x)这句话一定是主线程执行的 所以一定可以在主线程结束之前执行 并且 我们也能看到调用了拷贝构造函数 说明 此时已经将临时创建的A对象拷贝给了线程中的a
通过线程id证明是在主线程中创建的临时对象
通过在构造函数中加入获取线程id this_thread::get_id()判断对象的创建是在子线程中还是主线程中
class A {
public:
double x, y;
A()
{
cout << "构造函数" << endl;
cout << "线程的id:" << this_thread::get_id() << endl;
}
A(int i) {//类型转换构造函数
cout << "类型转换构造函数" << endl;
cout << "线程的id:" << this_thread::get_id() << endl;
x = i; y = 0;
}
A(const A& c) :x(c.x), y(c.y)
{
cout << "调用了拷贝构造函数" << endl;
cout << "线程的id:" << this_thread::get_id() << endl;
}
~A()
{
cout << "析构了" << endl;
cout << "线程的id:" << this_thread::get_id() << endl;
}
};
void start(const A& a)
{
cout << "进入子线程了" << endl;
cout << "子线程的id:" << this_thread::get_id() << endl;
}
int main()
{
cout << "主线程的id:" << this_thread::get_id() << endl;
int x = 1;
thread mobj(start, A(x));
//mobj.detach();
mobj.join();
//Sleep(10);
cout << "主线程结束" << endl;
return 0;
}
用类对象创建线程
当用类对象创建线程的时候 会复制一个类的对象到线程中去 所以如果主线程中的类对象销毁了 子线程中的类对象并不会被销毁
class A
{
public:
A()
{
cout << "A的构造函数"<<endl;
}
A(const A& a)
{
cout << "A的拷贝构造函数" << endl;
}
void operator()()
{
cout << "我是类";
}
~A()
{
cout << "A的析构函数" << endl;
}
};
int main()
{
A a;
thread mobj(a);
//mobj.detach();
mobj.join();
return 0;
}
会发现调用A的拷贝构造函数 说明是拷贝了一个A类对象到子线程中去
lambda表达式创建
auto mylambda = []{
cout<<"lambda执行开始"<<endl;
cout<<"lambda执行结束"<<endl;
};
thread mobj(mylambda);
类成员函数指针创建
注意成员函数前面必须加& 同样 这样传入的类对象一样会生成一个新的临时类对象 并不是将主线程中的类对象传入
class A {
public:
void thread_work(int &a)
{
cout << "进入子进程" << endl;
a = 2;
}
void operator()(int &a)//重载()操作符
{
cout << "进入子进程" << endl;
a = 2;
}
};
int main()
{
int x = 1;
A a;
thread mobj(&A::thread_work,a,ref(x));//类成员函数指针 类对象 传入参数
/*通过类成员函数重载() 实现线程创建*/
//thread mobj(a,x);//类成员函数指针 类对象 传入参数
mobj.join();
cout << "主线程中的x:" << x << endl;
cout << "主线程结束" << endl;
return 0;
}
join()
阻塞主线程 让主线程等待子线程执行完毕 然后子线程和主线程汇合
detach()
将子线程和主线程分离
void mprint()
{
cout << "第一个线程"<<endl;
cout << "第一个线程" << endl;
cout << "第一个线程" << endl;
cout << "第一个线程" << endl;
}
int main()
{
thread mobj(mprint);
mobj.detach();
cout << "main"<<endl;
cout << "main" << endl;
cout << "main" << endl;
cout << "main" << endl;
}
detach()后子线程和主线程失去联系 子线程驻留在后台运行 子线程由C++运行时库接管 子线程执行完成后由运行时库清理相关资源(守护线程) 也就是此时主线程可以和该子线程并发执行 与join相对立 在子线程detach后不能再join回来 会报错
joinable()
判断是否能使用join或者detach if(mobj.joinable() == true)
注意
在子线程中通过引用传递参数是没办法实现更改的
因为实际上编译器是会创建一个临时变量
void start(const int &a)
{
cout << "进入子线程了" << endl;
int& b = const_cast<int&>(a);
b = 2;
cout << "子线程结束" << endl;
}
int main()
{
int x = 1;//并没有变成2
thread mobj(start, x);
mobj.join();
cout << "主线程中的x:" << x << endl;
cout << "主线程结束" << endl;
return 0;
}
虽然用的是const int &a也就是引用方式 实际上子线程内部的a和主线程中的a地址不一样 并不是真的引用了 实际上是值传递 创建了一个临时值获取了传递进来的a的值
解决方法 std::ref()
void start(int &a)
{
a = 2;
}
int main()
{
int x = 1;
thread mobj(start, ref(x));
mobj.join();
cout<<x;//2