C++多线程
C++内置的线程库
头文件
#include <thread>
一、
创建一个函数myprint()
作为启动线程的内容用:
#include <iostream>
#include <thread>
using namespace std;
// 创建一个函数,作为线程内容
void myprint()
{
cout << "My thread starts." << endl;
cout << "My thread ends." << endl;
}
int main()
{
//thread: 1、创建线程;2、线程开始执行
thread mytobj(myprint);
//join(): 加入/汇合,阻塞主线程,让主线程等待子线程执行完毕,然后子线程和主线程汇合,然后主线程在往下走。
mytobj.join();//主线程阻塞到这里等待myprint执行完,当子线程执行完毕,这个join()就执行完毕,主线程就继续往下走
//注释join会报错,原因:可能发生主线程执行完毕,子线程没有执行完,程序不稳定
cout << "This is the main thread." << endl;
return 0;
}
thread
是线程类的名字
1.成员函数join()
起到阻塞线程的作用,意思为主线程阻塞到这里等待myprint()
执行完,当子线程执行完毕,这个join()
就执行完毕,主线程就继续往下走 。
2.成员函数detach()
的作用是,与主线程关联的thread对象就会失去与这个主线程的关联,此时这个子线程就会驻留在后台运行(主线程跟该子线程失去联系)。
**解释:**这个子线程就相当于被c++运行时库接管,档子线程执行完成后,由运行时库负责清理该线程相关的资源(守护线程)。
**注意:**一旦调用了detach(),就不能再join(),则否系统会报告异常
在代码中join()
或者detach()
一定要有一个,否则会报错。因为当主线程执行完毕时,子线程没有执行完,程序会不稳定。
detach()
示例:
#include <iostream>
#include <thread>
using namespace std;
// 创建一个函数,作为线程内容
void myprint()
{
cout << "My thread starts." << endl;
cout << "My thread ends." << endl;
}
int main()
{
//thread: 1、创建线程;2、线程开始执行
thread mytobj(myprint);
mytobj.detach();//一旦detach()后,与主线程关联的thread对象就会失去与这个主线程的关联,此时这个子线程就会驻留在后台运行(主线程跟该子线程失去联系)
//这个子线程就相当于被c++运行时库接管,档子线程执行完成后,由运行时库负责清理该线程相关的资源(守护线程)
//一旦调用了detach(),就不能再join(),则否系统会报告异常
//detach()使线程myprint失去我们自己的控制
cout << "This is the main thread." << endl;
return 0;
}
3.成员函数joinable()
的作用是:判断是否成功使用join()或者detach()的,返回true(可以join或者detach)或者false(不能join,不能detach)
示例:
#include <iostream>
#include <thread>
using namespace std;
// 创建一个函数,作为线程内容
void myprint()
{
cout << "My thread starts." << endl;
cout << "My thread ends." << endl;
}
int main()
{
//thread: 1、创建线程;2、线程开始执行
thread mytobj(myprint);
//在使用join之前加入joinable()判定,观察结果
if (mytobj.joinable())
{
cout << "join() can be used." << endl;
}
else
{
cout << "join() can not be used." << endl;
}
//join(): 加入/汇合,阻塞主线程,让主线程等待子线程执行完毕,然后子线程和主线程汇合,然后主线程在往下走。
mytobj.join();//主线程阻塞到这里等待myprint执行完,当子线程执行完毕,这个join()就执行完毕,主线程就继续往下走
//注释join会报错,原因:可能发生主线程执行完毕,子线程没有执行完,程序不稳定
//在使用join字后加入joinable()判定,观察结果
if (mytobj.joinable())
{
cout << "join() can be used." << endl;
}
else
{
cout << "join() can not be used." << endl;
}
cout << "This is the main thread." << endl;
return 0;
}
结果如图所示
二、类作为线程的可调用对象
#include <iostream>
#include <thread>
using namespace std;
// 二、创建一个类,可调用对象
class TA
{
public:
void operator()()//重载括号,不能带参数,使类作为一个可调用对象
{
cout << "我的线程operator()执行开始了" << endl;
//...
cout << "我的线程operator()执行结束了" << endl;
}
};
int main()
{
//二、将类对象添加至线程
TA ta;
thread mytobj3(ta);
mytobj3.join();
cout << "This is the main thread." << endl;
return 0;
}
执行结果
注意: 若类作为线程的可调用对象,使用detach()
,则类的构造函数的参数不能为引用&
构造函数如下:
//构造函数含引用
class TA
{
public:
TA(int &i):m_i(i) {}
int &m_i;
void operator()()//重载括号
{
cout << "m_i1的值为:" << m_i << endl;
cout << "m_i2的值为:" << m_i << endl;
cout << "m_i3的值为:" << m_i << endl;
cout << "m_i4的值为:" << m_i << endl;
cout << "m_i5的值为:" << m_i << endl;
cout << "m_i6的值为:" << m_i << endl;
}
};
则程序有可能出现问题,代码见下:
#include <iostream>
#include <thread>
using namespace std;
//构造函数含引用
class TA
{
public:
TA(int &i):m_i(i) {}
int &m_i;
void operator()()//重载括号
{
cout << "m_i1的值为:" << m_i << endl;
cout << "m_i2的值为:" << m_i << endl;
cout << "m_i3的值为:" << m_i << endl;
cout << "m_i4的值为:" << m_i << endl;
cout << "m_i5的值为:" << m_i << endl;
cout << "m_i6的值为:" << m_i << endl;
}
};
int main()
{
int myi = 6;
//二、将类对象添加至线程
TA ta(myi);
thread mytobj3(ta);
mytobj3.detach();
cout << "This is the main thread." << endl;
return 0;
}
该种情况:
1.使用detach()
当主线程执行完毕后,myi
的内存被销毁了,子线程还在打印myi
被回收内存的内容,可能会产生不可预料的结果。
2.问:一旦调用了detach(),那主线程执行结束了,这里用的ta这个对象还在吗?答:这个对象实际上是被复制到线程中去;执行完主线程后,ta会被销毁,但是复制的TA对象依旧存在。所以,只要这个TA类对象里没有引用,没有指针,就不会出问题。
改变构造函数,并添加拷贝构造函数来验证:
class TA
{
public:
int &m_i;
TA(int &i):m_i(i)
{
cout << "TA()构造函数被执行" << endl;
}
TA(const TA &ta) :m_i(ta.m_i)
{
cout << "TA()拷贝构造函数被执行" << endl;
}
~TA()
{
cout << "~TA()析构构造函数被执行" << endl;
}
void operator()()//重载括号
{
cout << "m_i1的值为:" << m_i << endl;
cout << "m_i2的值为:" << m_i << endl;
cout << "m_i3的值为:" << m_i << endl;
cout << "m_i4的值为:" << m_i << endl;
cout << "m_i5的值为:" << m_i << endl;
cout << "m_i6的值为:" << m_i << endl;
}
};
全程序:
#include <iostream>
#include <thread>
using namespace std;
class TA
{
public:
int &m_i;
TA(int &i):m_i(i)
{
cout << "TA()构造函数被执行" << endl;
}
TA(const TA &ta) :m_i(ta.m_i)
{
cout << "TA()拷贝构造函数被执行" << endl;
}
~TA()
{
cout << "~TA()析构构造函数被执行" << endl;
}
void operator()()//重载括号
{
cout << "m_i1的值为:" << m_i << endl;
cout << "m_i2的值为:" << m_i << endl;
cout << "m_i3的值为:" << m_i << endl;
cout << "m_i4的值为:" << m_i << endl;
cout << "m_i5的值为:" << m_i << endl;
cout << "m_i6的值为:" << m_i << endl;
}
};
int main()
{
int myi = 6;
//二、将类对象添加至线程
TA ta(myi);
thread mytobj3(ta);
mytobj3.detach();
cout << "This is the main thread." << endl;
return 0;
}
执行结果
证明TA类的对象ta在被线程调用时被复制了。执行的析构函数是主线程的ta对象
当把mytobj3.detach()
替换为mytobj3.join()
,运行的结果为:
析构函数执行了两次,再一次证明了TA类的对象ta在被线程调用时被复制了。第一个析构为线程执行完,线程中ta执行的析构,第二个析构主线程ta对象释放的。
三、用lambda表达式创建线程
#include <iostream>
#include <thread>
using namespace std;
int main()
{
auto mylamthread = [] {
cout << "我的线程3开始执行了" << endl;
//...
cout << "我的线程3执行结束了" << endl;
};
thread myobj4(mylamthread);
myobj4.join();
cout << "This is the main thread." << endl;
return 0;
}
运行结果: