- 一、创建和等待多个线程:
a)多个线程执行程序是乱的,跟操作系统内部对线程的运行调度机制有关
b)主线程等待所有子线程运行结束后,最后主线程结束,推荐使用join,更容易写出稳定的程序
c)把thread对象放入容器里,便于管理 - 二、数据共享问题
a)只读的数据:是安全稳定的,不需要特别的处理手段,直接读就可以
b)有读有写:如果代码没有特别处理,程序必然崩溃。 最简单的处理方法是,读的时候不能写,写的时候不能读,所有写的线程不能同时写,所有读的线程不能同时读;
c) 保护共享数据:操作时,某个线程用代码把共享数据锁住、操作数据、解锁, 其他想操作共享数据的线程必须等待解锁,锁定、操作、解锁 - 三、互斥量(mutex)的基本概念:
a)互斥量是一个类对象。理解成一把锁,多个线程尝试用lock()成员函数来加锁,只能有个线程能锁定成功(成功的线程才对内存具有操作权限),如果没成就会卡在lock这里不断尝试。
b)互斥量使用要小心,保护数据不多也不少,少了,没达到保护效果,多了影响效率。
c)互斥量用法:先lock(),操作共享数据,unlock,lock()与unlock()要成对使用,每调用一次lock()就必须调用一次unlock()
d)std::lock_guard():可以替代lock()与unlock(),也就是说,用了lock_guard之后就不用再使用lock()与unlock()
e)lock_guard每次到return才执行unlock的功能,如果想让lock_guard提前结束,可以用{…} - 四、死锁
- 原因:
a)两个互斥量,上锁顺序不一致,线程A先mutex1上锁然后mutex2上锁,线程B先mutex2上锁然后mutex1上锁。
b)当线程 mutex1上锁之后,需要进行mutex2上锁,但此时,线程B正处于mutex2上锁状态,因此线程A无法向下运行。
c)当线程 mutex2上锁之后,需要进行mutex1上锁,但此时,线程A正处于mutex1上锁状态,因此线程B无法向下运行。
d)形成两个线程都无法向下运行的局面 - 解决方案:
a)只要保证这两个互斥量上锁的顺序一致就不会死锁
b)使用std::lock()一次性锁住多个互斥量,例std::lock(mutex1,mutex2),如果其中有一个没有锁住,则会把另外锁住的也放开,不会造成死锁现象。
c)使用std::lock()必须单独手动调用mutex1.unlock()函数解锁
d)或者在调用std::lock()后,紧接着使用std::lock_guardstd::mutex myguard(mutex1,std::adopt::lock),adopt::lock会自动释放互斥量
#include <iostream>
#include <thread>
#include <vector>
#include <iterator>
#include <list>
#include <mutex>
using namespace std;
void myPrint(int num)
{
cout << "Thread " << num << " is beginnig!" << " Thread_ID= " << std::this_thread::get_id() << endl;
for(int i=0;i<1000000;i++)
{
cout<<num<<endl;
}
cout << "Thread " <<num << " is over" << " Thread_ID= " << std::this_thread::get_id() << endl;
}
class A {
public:
void myPrint_A(int num)
{
cout << "Thread " << (char)num << " is beginnig!" << " Thread_ID= " << std::this_thread::get_id() << endl;
for(int i=0;i<1000000;i++)
{
myMutex1.lock();
myMutex2.lock();
cout<<(char)num<<endl;
myMutex2.unlock();
myMutex1.unlock();
}
cout << "Thread " <<(char) num << " is over" << " Thread_ID= " << std::this_thread::get_id() << endl;
}
void myPrint_B(int num)
{
cout << "Thread " << (char)num << " is beginnig!" << " Thread_ID= " << std::this_thread::get_id() << endl;
for(int i=0;i<1000000;i++)
{
myMutex2.lock();
myMutex1.lock();
cout<<(char)num<<endl;
myMutex1.unlock();
myMutex2.unlock();
}
cout << "Thread " << (char)num << " is over" << " Thread_ID= " << std::this_thread::get_id() << endl;
}
void myPrint_C(int num)
{
cout << "Thread " << (char)num << " is beginnig!" << " Thread_ID= " << std::this_thread::get_id() << endl;
for(int i=0;i<1000000;i++)
{
std::lock(myMutex1, myMutex2);
cout<<(char)num<<endl;
myMutex2.unlock();
myMutex1.unlock();
}
cout << "Thread " <<(char) num << " is over" << " Thread_ID= " << std::this_thread::get_id() << endl;
}
void myPrint_D(int num)
{
cout << "Thread " << (char)num << " is beginnig!" << " Thread_ID= " << std::this_thread::get_id() << endl;
for(int i=0;i<1000000;i++)
{
std::lock(myMutex1, myMutex2);
cout<<(char)num<<endl;
myMutex1.unlock();
myMutex2.unlock();
}
cout << "Thread " << (char)num << " is over" << " Thread_ID= " << std::this_thread::get_id() << endl;
}
void writeMsg()
{
cout << "writeMsgthis " << this << endl;
cout << "writeMsgmsgQueue_addr " << &(this->msgQueue) << endl;
cout << "Thread writeMsg() is beginnig!" << " Thread_ID= " << std::this_thread::get_id() << endl;
for (int i=0; i<1000000000; i++)
{
cout << "Write " << i << " to msgQueue" << endl;
myMutex.lock();
msgQueue.push_back(i);
myMutex.unlock();
}
cout << "Thread writeMsg() is over!" << " Thread_ID= " << std::this_thread::get_id() << endl;
}
void readMsg()
{
cout << "readMsg_this " << this << endl;
cout << "readMsgmsgQueue_addr " << &(this->msgQueue) << endl;
cout << "Thread readMsg() is beginnig!" << " Thread_ID= " << std::this_thread::get_id() << endl;
for (int i = 0; i<1000000000; i++)
{
if (!msgQueue.empty())
{
{
std::lock_guard<std::mutex> myLockGuard(myMutex);
int cmd = msgQueue.front();
cout << "read the first one in msgQueue is " << cmd << endl;
msgQueue.pop_front();
}
}
else {
cout << "msgQueue is empty,i= " << i << endl;
}
}
cout << "Thread readMsg() is over!" << " Thread_ID= " << std::this_thread::get_id() << endl;
}
private:
list<int> msgQueue;
mutex myMutex;
mutex myMutex1;
mutex myMutex2;
};
void example1()
{
vector<thread> myThread;
for (int i = 0; i<10; i++)
{
myThread.push_back(thread(myPrint, i));
}
for (vector<thread>::iterator iter = myThread.begin(); iter != myThread.end(); iter++)
{
iter->join();
}
}
void example2()
{
A myobj;
thread writeThread(&A::writeMsg, &myobj);
thread readThread(&A::readMsg, &myobj);
writeThread.join();
readThread.join();
}
void example3()
{
A myobj;
thread Thread_A(&A::myPrint_A,&myobj,(int) 'A');
thread Thread_B(&A::myPrint_B,&myobj, (int)'B');
Thread_A.join();
Thread_B.join();
}
void example4()
{
A myobj;
thread Thread_A(&A::myPrint_C,&myobj,(int) 'C');
thread Thread_B(&A::myPrint_D,&myobj, (int)'D');
Thread_A.join();
Thread_B.join();
}
int main()
{
cout << "Main thread is begining!" << endl;
example4();
cout << "Main thread is over!" << endl;
return 0;
}