记录:学习多线程(四):互斥量概念、用法、死锁演示及解决方案

网站UI设计

  互斥量的基本概念

  互斥量的用法

  死锁

  #include<map>#include<string>#include<thread>#include<vector>#include<list>#include<mutex>using namespace std;class A{public://把收到的消息(玩家命令)入到一个队列的线程void inMsgRecvQueue(){//unlock()for(int i=0;i<100000; ++i){cout<<"inMsgRecvQueue()执行,插入一个元素"<<i<<l;//{//std::lock_guard<std::mutex> sbguard(my_mutex)//my_mutex1.lock();/*实际工程中这两个锁头可能不一定挨着,可能他们需要保护不同的数据共享块*///my_mutex2.lock();std::lock(my_mutex1,my_mutex2);std::lock_guard<std::mutex> sbguard1(my_mutex1,std::adopt_lock)std::lock_guard<std::mutex> sbguard2(my_mutex2,std::adopt_lock)msgRecvQueue.push_back(i);/*假设这个数字i就是我们收到的命令我直接弄到消息队列里边来*///my_mutex2.unlock();//my_mutex1.unlock();//}//my_mutex.unlock();//......//其他处理代码}}bool outMsgLULproc(int &command){//std::lock_guard<std::mutex> sbguard(my_mutex1)/*sbguard随便起的对象名,lock_guard构造函数里执行了mutex::lock(),析构的时候调用了mutex::unlock()*///my_mutex1.lock();//my_mutex2.lock();std::lock(my_mutex1,my_mutex2);std::lock_guard<std::mutex> sbguard1(my_mutex1,std::adopt_lock);std::lock_guard<std::mutex> sbguard2(my_mutex2,std::adopt_lock);if(!msgRecvQueue.empty()){//消息队列不为空int command = msgRecvQueue.front();/*返回第一个元素,但不检查元素是否存在*/msgRecvQueue.pop_front();//移除第一个元素,但不返回;//这里就考虑处理数据...//my_mutex1.unlock();//my_mutex2.unlock();return true;}//my_mutex1.unlock();//my_mutex2.unlock();return false;}//把数据从消息队列中取出的线程void outMsgRecvQueue(){int command = 0;for(int i=0;i<100000;i++){bool result = outMsgLULproc(command);if(result == true){cout<<"outMsgRecvQueue()执行,取出一个元素"<<commmand<<l;/*可以考虑进行命令(数据)处理*/}else{cout<<"outMsgRecvQueue()执行,但目前消息队列中为空"<<i<<l;}}cout<<""<<l;}private:std::list<int> msgRecvQueue: //容器,专门用于代表玩家给咱们发送过来的命令std::mutex my_mutex1;//创建一个互斥量,一个互斥量是一把锁std::mutex my_mutex2;//创建一个互斥量};int main(){A myobja;std::thread myOutMsgObj(&A::outMsgRecvQueue,&myobja);/*第二个参数是引用,才能保证线程里用的是用一个对象*/std:;thread myInMsgObj(&A::inMsgRecvQueue,&myobja);myOutMsgObj.join();myInMsgObj.join();return 0;}//保护共享数据,操作时,某个线程用代码把共享数据锁住,其他想操作共享数据的必须等待解锁

  //互斥量的基本概念

  //互斥量是个类对象。理解成一把锁,多个线程尝试用lock()成员函数来加锁,只有一个线程可以锁定成功(成功的标志是lock()函数返回),如果没锁成功,那么流程卡在lock()这里不断的尝试去锁这个锁头;

  //互斥量使用要小心,保护数据不多也不少,少了,没起到保护效果,多了,影响效率。

  //互斥量的用法

  //lock(),unlock(0;

  //步骤:先lock(),操作共享数据,unlock();

  //lock()和unlock()要成对使用,有lock()必然要有unlock,每调用一次unlock();

  //不应该也不允许调用一次lock却调用二次unlock,不允许调用二次lock却调用一次unlock,这些非对称数量的调用到会导致代码不稳定。

  //有lock,忘记unlock的问题,非常难排查;

  //为防止大家忘记unlock(),引了一个脚std::lock_guard的类模板,你忘记了unlock不要紧,我替你unlock();

  //学习过智能指针(unique_ptr<>):你忘记释放内存不要紧,我给你释放;保姆;

  //std:lock_guard类模板:直接取代lock()和unlock();也就是说,用了lock_guard之后,再不能用lock()和unlock();

  //死锁

  //张三:站在北京等李四,不挪窝;

  //李四:站在深圳等张三,不挪窝;

  //c++中:

  //比如有两把锁(死锁这个问题 是由至少两个锁头也就是两个互斥量才能产生)

  //金锁(jinlock),银锁(yinlock);

  //两个线程A,B

  a//线程A执行的时候,这个线程先锁金锁,把金锁lock(),然后它去lock银锁。。。

  //出现了上下文切换

  b//线程B执行了,这个线程先锁银锁,因为银锁没有被锁,所以银锁会lock()成功,线程B要去lock金锁。。。。

  //此时此刻,死锁就产生了。。。

  c//线程A因为拿不到银锁头,流程走不下去(所有后边代码有解锁金锁头的但是流程走不下去,所以金锁头解不开)

  d//线程B因为拿不到金锁头,流程走不下去(所有后边代码有解锁银锁头的但是流程走不下去,所以银锁头解不开)

  //死锁的一般解决方案

  //只要保证这两个互斥量上锁的顺序一致,就不会死锁。

  //std::lock()函数模板

  (用来处理多个互斥量的时候才出场)

  //能力:一次锁住两个或者两个以上的互斥量(至少两个,多了不限,1个不行);

  //它不存在这种因为在多个线程中,因为锁的顺序问题导致死锁的风险问题。

  //std::lock():如果互斥量中有一个没锁住,它就在那里等着,等所有互斥量都锁住,它才能往下走。

  //要么两个互斥量都锁住,要么两个互斥量都没锁住。如果只锁了一个,另外一个没有锁成功,则它立即把已经锁住的解锁。

  //std::lock_guard的std:;adopt_lock

  std::adopt_lock是个结构体对象,起一个标记作用:作用就是表示这个互斥量已经lock(),不需要再std::lock_guardstd::mutex里面对对象进行再次lock()了。

  //总结:std::lock(),一次锁多个互斥量;谨慎使用(建议一个一个锁);

标签: 网站UI设计