线程创建
#include <iostream>
#include <thread>
using namespace std;
void myPrint()
{
cout << "我的线程开始运行" << endl;
cout << "我的线程运行完毕" << endl;
return;
}
class Ta
{
public:
void operator()() //不能带参数
{
cout << "我的线程开始运行" << endl;
//-------------
//-------------
cout << "我的线程运行完毕" << endl;
}
};
class Data_
{
public:
void GetMsg(){}
void SaveMsh(int a){std::cout << a << endl;}
};
int main()
{
// 方式1
//(1)创建了线程,线程执行起点(入口)是myPrint;(2)执行线程
thread myThread(myPrint);
//(2)阻塞主线程并等待myPrint执行完,当myPrint执行完毕,join()就执行完毕,主线程继续往下执行
//join意为汇合,子线程和主线程回合
myThread.join();
// 方式2 创建一个类,并编写圆括号重载函数,初始化一个该类的对象,把该对象作为线程入口地址
Ta ta;
thread myThread(ta);
myThread.join();
//方式3 lambda
auto lambdaThread = [] {
cout << "我的线程开始执行了" << endl;
//-------------
//-------------
cout << "我的线程开始执行了" << endl;
};
thread myThread(lambdaThread);
myThread.join();
//方式4 把某个类中的某个函数作为线程的入口地址
//第一个&意思是取址,第二个&意思是引用,相当于std::ref(s)
//thread oneobj(&Data_::SaveMsh,s)传值也是可以的
//在其他的构造函数中&obj是不会代表引用的,会被当成取地址
//调用方式:对象成员函数地址,类实例,[成员函数参数]
//第二个参数可以传递对象s,也可以传递引用std::ref(s)或&s
//传递s,会调用拷贝构造函数在子线程中生成一个新的对象
//传递&,子线程中还是用的原来的对象,所以就不能detach,因为主线程运行完毕会把该对象释放掉
Data_ s;
thread oneob (&Data_::SaveMsh,s,122);
thread twoobj (&Data_::GetMsg,&s);
oneobj.join();
twoobj.join();
cout << "Hello World!" << endl;
return 0;
}
线程传参详解,detach()大坑,成员函数做线程函数
一、传递临时对象作为线程参数
1.1要避免的陷阱1:
#include <iostream>
#include <thread>
using namespace std;
//void myPrint(const int i, char* pmybuf)
void myPrint(const int &i, char* pmybuf)
{
//如果线程从主线程detach了
//i不是mvar真正的引用,实际上值传递,即使主线程运行完毕了,子线程用i仍然是安全的,但仍不推荐传递引用
//推荐改为const int i
cout << i << endl;
//pmybuf还是指向原来的字符串,所以这么写是不安全的
cout << pmybuf << endl;
}
int main()
{
int mvar = 1;
int& mvary = mvar;
char mybuf[] = "this is a test";
thread myThread(myPrint, mvar, mybuf);//第一个参数是函数名,后两个参数是函数的参数
myThread.join();
//myThread.detach();
cout << "Hello World!" << endl;
}
1.2要避免的陷阱2:
#include <iostream>
#include <thread>
#include <string>
using namespace std;
void myPrint(const int i, const string& pmybuf)
{
cout << i << endl;
cout << pmybuf << endl;
}
int main()
{
int mvar = 1;
int& mvary = mvar;
char mybuf[] = "this is a test";
//如果detach了,这样仍然是不安全的
//因为存在主线程运行完了,mybuf被回收了,系统采用mybuf隐式类型转换成string
//推荐先创建一个临时对象thread myThread(myPrint, mvar, string(mybuf));就绝对安全了。。。。
thread myThread(myPrint, mvar, mybuf);
myThread.join();
//myThread.detach();
cout << "Hello World!" << endl;
}
1.3总结
- 如果传递int这种简单类型,推荐使用值传递,不要用引用
- 如果传递类对象,避免使用隐式类型转换,全部都是创建线程这一行就创建出临时对象,然后在函数参数里,用引用来接,否则还会创建出一个对象
- 终极结论:建议不使用detach
二、传递类对象、智能指针作为线程参数
#include <iostream>
#include <thread>
using namespace std;
class A {
public:
mutable int m_i; //m_i即使实在const中也可以被修改
A(int i) :m_i(i) {}
};
//myPrint(const A& pmybuf)中引用不能去掉,如果去掉会多创建一个对象
//const也不能去掉,去掉会出错
void myPrint(const A& pmybuf)
{
pmybuf.m_i = 199;
cout << "子线程myPrint的参数地址是" << &pmybuf << "thread = " << std::this_thread::get_id() << endl;
}
//智能指针
void myPrint(unique_ptr<int> ptn)
{
cout << "thread = " << std::this_thread::get_id() << endl;
}
int main()
{
A myObj(10);
//myPrint(const A& pmybuf)中引用不能去掉,如果去掉会多创建一个对象
//const也不能去掉,去掉会出错
//即使是传递的const引用,但在子线程中还是会调用拷贝构造函数构造一个新的对象,
//所以在子线程中修改m_i的值不会影响到主线程
//如果希望子线程中修改m_i的值影响到主线程,可以用thread myThread(myPrint, std::ref(myObj));
//这样const就是真的引用了,myPrint定义中的const就可以去掉了,类A定义中的mutable也可以去掉了
thread myThread(myPrint, myObj);
myThread.join();
//myThread.detach();
unique_ptr<int> up(new int(10));
//独占式指针只能通过std::move()才可以传递给另一个指针
//传递后up就指向空,新的ptn指向原来的内存
//所以这时就不能用detach了,因为如果主线程先执行完,ptn指向的对象就被释放了
thread myThread1(myPrint, std::move(up));
myThread1.join();
cout << "Hello World!" << endl;
}