C++11的多线程并发编程(三)
疫情确诊加疑似都快7w+了,能做的也只能在家大门不出,二门不迈了,写一篇继续记录c++11的多线程并发编程
- 临时对象在线程中调用的问题
在创建的子线程中,调用类的对象时考虑到在主线程定义参数执行带参构造函数时,子线程detach时,主线程线执行完清除变量,那么一般在创建主线程时直接构造对象,并在输入函数出增加引用。这样就可以保证,对象带参构造执行于主线程,举个例子:
#include <iostream>
#include <thread>
using namespace std;
class A
{
public:
int i;
A(int a):i(a){cout << "createFunction is running"<<"thread id is:"<< this_thread::get_id() << endl;}
A(const A &a):i(a.i){cout << "copyFunction is running" <<"thread id is:" <<this_thread::get_id() << endl;}
~A(){cout << "cancelFunction is running" << "thread id is" << this_thread::get_id() << endl;}
};
void myFunc(const A &a)
{
cout <<"parameter is :"<< a.i << endl;
}
int main()
{
cout <<"the main thread id is:"<< this_thread::get_id() << endl;
int par = 99;
thread mythread(myFunc, A(par));
mythread.detach();
return 0;
}
可以看到带参构造函数执行于主线程。因此,如果在创建线程的时候传递简单类型参数,例如,int,那么就直接值传递;如果是传递类对象,就像上例,建议都在创建线程的时候构建出临时对象来,然后在调用函数入口处,用引用参数接。
例如,当我们要调用函数对类的成员进行改动,那么就会出现这样的问题:
#include <iostream>
#include <thread>
using namespace std;
class A
{
public:
mutable int i;
A(int a):i(a){cout << "createFunction is running"<<"thread id is:"<< this_thread::get_id() << endl;}
A(const A &a):i(a.i){cout << "copyFunction is running" <<"thread id is:" <<this_thread::get_id() << endl;}
~A(){cout << "cancelFunction is running" << "thread id is" << this_thread::get_id() << endl;}
};
void myFunc(const A &a)
{
a.i = 100;
cout <<this_thread::get_id()<<"thread's parameter is :"<< a.i << endl;
}
int main()
{
cout <<"the main thread id is:"<< this_thread::get_id() << endl;
int par = 99;
A a(par);
thread mythread(myFunc, a);
mythread.join();
cout <<this_thread::get_id()<< "thread's paramter is : "<<a.i << endl;
return 0;
}
我们预期的情况,我在调用函数,参数为引用对象,那么这个a.i的值应该就是100,但是会有两个线程两个不同的成员值,因此,证明了之前讲的,他还是进行了拷贝构造函数,新的对象的成员值了
解决办法只要在创建输入参数的时候加上ref();就可以出现同一个对象
- 传参为智能指针以及类成员函数
这个举个例子,演示当创建线程的时候,智能指针和类成员函数为参数的时候,执行情况:
#include<iostream>
#include<thread>
using namespace std;
class A
{
public:
int myNum;
A(int num):myNum(num)
{
cout << "createFunction is running" << num << endl;
}
A(const A &a):myNum(a.myNum)
{
cout << "copyFunction is running" << endl;
}
void myFunction(int num)
{
myNum = num;
cout << "memberFunction is running" <<myNum <<endl;
}
~A()
{
cout << "cancelFunction is running" << endl;
}
};
void myFunc(unique_ptr<int> ptr)
{
cout << "ptr point to :" << *ptr << endl;
return;
}
int main(int argc, char* argv[])
{
unique_ptr<int> ptrq(new int(100));
thread mythread1(myFunc, move(ptrq));
A a(999);
thread mythread2(&A::myFunction, a, 99);
mythread1.join();
mythread2.join();
cout << "the main thread is end" << endl;
return 0;
}
通过运行结果可以看到,两个线程同步运行,会出现交叉情况,main线程是最后结束,成员函数运行成功,且智能指针指向的也传递成功,当指针move操作之后,ptr已经失效了,访问则会报错;
- 多线程问题
这里举个多线程针对全局数据的访问案例:
#include <iostream>
#include <vector>
#include <thread>
using namespace std;
vector<int> parameter = {1,2,3};
void myFunc(int num)
{
cout << num << "+" << parameter[0] << parameter[1] << parameter[2] << endl;
return;
}
int main()
{
vector<thread> vecThread;
for(int i = 0; i < 10; i++)
{
vecThread.push_back(thread(myFunc, i));
}
for(auto iter = vecThread.begin(); iter != vecThread.end(); iter++)
{
iter->join();
}
cout << "main thread is end;" << endl;
return 0;
}
运行结果显示,当多个线程共同读取全局变量时,能够有效的进行,
上面这个例子,是针对只读数据,是安全的,不需要其他处理,但是如果我一个线程要读,而另外一个线程要写同一个数据,那么会出现什么情况,在操作系统切换的过程中坑定会崩溃,借鉴个案例:
一个车票窗口有两个线程,一个接受乘客排队的购买指令,另一个负责输出乘客指令进行分析指令进行处理,那么根据排队顺序来,需要一个队列来从尾部存储指令,然后从头部输出指令,因此以备两个进程进行读写。
#include <iostream>
#include <thread>
#include <list>
using namespace std;
class A
{
public:
void inCommand()
{
for(int i = 0; i < 1000; i++)
{
ticket.push_back(i);
cout << "a command is comming" << endl;
}
}
void outCommand()
{
for(int j = 0; j < 1000; j++)
{
if(!ticket.empty())
{
int command = ticket.front();
ticket.pop_front();
cout << "command is :" << command << endl;
}
else
{
cout << "there is no people" << endl;
}
}
}
private:
list<int> ticket;
};
int main(int argc, char *argv[])
{
A a;
thread thread1(&A::inCommand, &a);
thread thread2(&A::outCommand, &a);
thread1.join();
thread2.join();
cout << "the main thread is end;" << endl;
return 0;
}
当运行的时候,什么情况都有,有按顺序来的,有一直都显示没有人,然后全是人排队,因此,下一个开始记录,如何互斥的访问这个list