共享数据的保护案例:
网络游戏服务 有两个线程:一个线程用来收集玩家命令,并把数据写到一个 ;一个线程从队列中取出玩家需要的动作。
用成员函数来作为线程函数的方法来写线程:
保护共享数据问题得第一个概念: 互斥量,保护共享数据,操作的时候 摸个线程用代码将共享数据锁住,操作数据,解锁,在锁的同时其他共享数据的线程都在等待。
互斥量(mutex):mutex 是一个类对象,可以理解成一把锁,当多个线程尝试用lock() 成员函数来加锁的时候,只有一个线程能锁定成功,如果没有锁成功,那么流程卡在lock () 这里不断的尝试去锁。
互斥量使用的是要小心,保护的数据不能太多也不能太少
互斥量的使用: lock unlock成对使用,并且每个互斥量就是一把锁,如果有两个互斥量,那就是两把锁
#if 1 // 有读有写 2个线程在读 8个线程在写
class A
{
public :
// 吧收到的消息 入到一个队列当中
void inMsgRecvQueue()
{
for (size_t i = 0; i < 10000; i++)
{
cout << "inMsgRecvQueue() 执行,插入一个元素 " << i << endl;
// 用互斥量来加锁
// ==================================
my_mutex.lock();
msgRecvQueue.push_back(i);
my_mutex.unlock();
// ==================================
}
}
bool outMSG( int &cmd)
{
// ==================================
my_mutex.lock();
if (msgRecvQueue.empty()==false)
{
cmd = msgRecvQueue.front();
msgRecvQueue.pop_front();
my_mutex.unlock();
// ==================================
return true;
}
my_mutex.unlock();
// ==================================
return false;
}
// 另外一个线程
void getMsgRecvQueue()
{
int cmd=0;
for (size_t i = 0; i < 10000; i++)
{
bool result = outMSG(cmd);
if (result==true)
{
cout << " getMsgRecvQueue(): sss " << cmd<< endl;
}
else
{
cout<<" getMsgRecvQueue(): 是空的 " << endl;
}
}
}
private:
list msgRecvQueue;
mutex my_mutex; // 创建一个互斥量
};
int main()
{
A a;
thread mythread(&A::getMsgRecvQueue,&a); // 第二对象是 &
thread mythread2(&A::inMsgRecvQueue, &a);
mythread.join();
mythread2.join();
cout << "Hello World!\n";
}
#endif
多个(至少两个)锁头之间发生的相互等待的情况。
案例分析: 有连个锁 lock 1 lock 2,有两个线程A B
A线程:执行的时候lock1先锁(不解锁)然后 lock 2 上锁。
B线程:先lock2 上锁后 (不解锁),然后去lock1
结果就是: A线程 要用lock 2 ,但是lock 2 被B线程锁定
B线程 要用lock 1 ,但是lock1被A 线程锁定
#if 1 //死锁 形成 class A { public: // 吧收到的消息 入到一个队列当中 void inMsgRecvQueue() { for (size_t i = 0; i < 10000; i++) { cout << "inMsgRecvQueue() 执行,插入一个元素 " << i << endl; // 用互斥量来加锁 // ================================== // in线程先lock 1 (不解锁),然后接着lock2 my_mutex.lock(); my_mutex2.lock(); msgRecvQueue.push_back(i); my_mutex.unlock(); my_mutex2.unlock(); // ================================== } } bool outMSG(int& cmd) { // ================================== // out 线程 先锁 2 不解锁,直接lock 1 , my_mutex2.lock(); my_mutex.lock(); if (msgRecvQueue.empty() == false) { cmd = msgRecvQueue.front(); msgRecvQueue.pop_front(); my_mutex.unlock(); my_mutex2.unlock(); // ================================== return true; } my_mutex.unlock(); my_mutex2.unlock(); // ================================== return false; } // 另外一个线程 void getMsgRecvQueue() { int cmd = 0; for (size_t i = 0; i < 10000; i++) { bool result = outMSG(cmd); if (result == true) { cout << " getMsgRecvQueue(): sss " << cmd << endl; } else { cout << " getMsgRecvQueue(): 是空的 " << endl; } } } private: list
msgRecvQueue; mutex my_mutex; // 创建一个互斥量 mutex my_mutex2; // 创建一个互斥量 }; int main() { A a; thread mythread(&A::getMsgRecvQueue, &a); // 第二对象是 & thread mythread2(&A::inMsgRecvQueue, &a); mythread.join(); mythread2.join(); cout << "Hello World!\n"; } #endif
死锁的解决方法: 多线程中锁的顺序保持一致
lock的使用:可以锁多个互斥量,为了解决一些能锁住,一些锁不住的问题。
lock(my_mutex, my_mutex2);
#if 1 //lock
class A
{
public:
// 吧收到的消息 入到一个队列当中
void inMsgRecvQueue()
{
for (size_t i = 0; i < 10000; i++)
{
cout << "inMsgRecvQueue() 执行,插入一个元素 " << i << endl;
// 用互斥量来加锁
// ==================================
// in线程先lock 1 (不解锁),然后接着lock2
// my_mutex.lock();
// my_mutex2.lock();
lock(my_mutex, my_mutex2);
msgRecvQueue.push_back(i);
my_mutex.unlock();
my_mutex2.unlock();
// ==================================
}
}
bool outMSG(int& cmd)
{
// ==================================
// out 线程 先锁 2 不解锁,直接lock 1 ,
//my_mutex2.lock();
//my_mutex.lock();
lock(my_mutex, my_mutex2);
if (msgRecvQueue.empty() == false)
{
cmd = msgRecvQueue.front();
msgRecvQueue.pop_front();
my_mutex.unlock();
my_mutex2.unlock();
// ==================================
return true;
}
my_mutex.unlock();
my_mutex2.unlock();
// ==================================
return false;
}
// 另外一个线程
void getMsgRecvQueue()
{
int cmd = 0;
for (size_t i = 0; i < 10000; i++)
{
bool result = outMSG(cmd);
if (result == true)
{
cout << " getMsgRecvQueue(): sss " << cmd << endl;
}
else
{
cout << " getMsgRecvQueue(): 是空的 " << endl;
}
}
}
private:
list msgRecvQueue;
mutex my_mutex; // 创建一个互斥量
mutex my_mutex2; // 创建一个互斥量
};
int main()
{
A a;
thread mythread(&A::getMsgRecvQueue, &a); // 第二对象是 &
thread mythread2(&A::inMsgRecvQueue, &a);
mythread.join();
mythread2.join();
cout << "Hello World!\n";
}
#endif
adopt_lock 是一个结构体对象,起一个标价的作用,表示这个互斥量已经被锁定
unique_lock 是一个类模板,在工作中,一般lock_guard (推荐使用,其取代了mutex.lock /unlock)
unique_lock比较灵活,同时占用的内存也比较多,运行时间也比较长。
lock_guard的使用:
前必须要lock()
lock_guard mylock(my_mutex, adopt_lock); // 第二个参数 其一个标记的作用,表示这个mutex 已经被锁定
unique_lock 的使用,也可以是adopt_lock,其意思就是这个已经被上锁,不希望再次上锁;
不能自己lock()
unique_lock mylock2(my_mutex,try_to_lock);
/ unique_lock
mylock2(my_mutex,try_to_lock); 的功能就是:我们会去尝试用lock 去锁定这个mutex,如果没有锁定成功我们将立即返回,不会阻塞到哪里,用try_to_lock 的前提是不能自己去上锁
#if 1 //unique_lock
// unique_lock mylock2(my_mutex,try_to_lock); 的功能就是:我们会去尝试用lock 去锁定这个mutex,如果没有锁定成功我们将立即返回,不会阻塞到哪里,用try_to_lock 的前提是不能自己去上锁
class A
{
public:
// 吧收到的消息 入到一个队列当中
void inMsgRecvQueue()
{
for (size_t i = 0; i < 10000; i++)
{
cout << "inMsgRecvQueue() 执行,插入一个元素 " << i << endl;
unique_lock mylock2(my_mutex, try_to_lock); // try_to_lock 尝试锁定这个互斥量mutex
if (mylock2.owns_lock()) // 如果已经锁定, 处理数据,没有锁定直接返回
{
msgRecvQueue.push_back(i);
}
else
{
cout << "inMsgRecvQueue() 执行,但是没有拿到锁===================================================================== " << endl;
}
// ==================================
}
}
bool outMSG(int& cmd)
{
// ==================================
// out 线程 先锁 2 不解锁,直接lock 1 ,
//my_mutex2.lock();
//my_mutex.lock();
// lock_guard mylock(my_mutex, adopt_lock); // 第二个参数 其一个标记的作用,表示这个mutex 已经被锁定 ,
// my_mutex.lock();
unique_lock mylock2(my_mutex,try_to_lock);
chrono::microseconds d(200);
this_thread::sleep_for(d);
if (msgRecvQueue.empty() == false)
{
cmd = msgRecvQueue.front();
msgRecvQueue.pop_front();
// ==================================
return true;
}
// adopt_lock 是一个结构体对象,起一个标价的作用,表示这个互斥量已经被锁定
// ==================================
return false;
}
// 另外一个线程
void getMsgRecvQueue()
{
int cmd = 0;
for (size_t i = 0; i < 10000; i++)
{
bool result = outMSG(cmd);
if (result == true)
{
cout << " getMsgRecvQueue(): sss " << cmd << endl;
}
else
{
cout << " getMsgRecvQueue(): 是空的 " << endl;
}
}
}
private:
list msgRecvQueue;
mutex my_mutex; // 创建一个互斥量
};
int main()
{
A a;
thread mythread(&A::getMsgRecvQueue, &a); // 第二对象是 &
thread mythread2(&A::inMsgRecvQueue, &a);
mythread.join();
mythread2.join();
cout << "Hello World!\n";
}
#endif
必须要自己加锁:
1、lock()
2、unlock()
3、try_lock(),我们尝试去加锁,如果成功就返回true ,如果不成功就返回FALSE
4、release() ,返回她所管理的mutex对象的指针,并释放所有权,