• 五、互斥量概念、用法、死锁


    一、互斥量mutex

    保护共享数据,操作时,用代码把共享数据锁住,操作数据,解锁。其他线程要操作共享数据的线程必须等待解锁,锁住,操作,解锁。

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

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

    二、互斥量用法

    1、lock()、unlock()

    include <mutex>

    先lock,操作共享数据,再unlock。

    使用规则:成对使用,有lock必须有unlock;

     1 #include <iostream>
     2 #include <thread> //线程
     3 #include <vector>
     4 #include <list>
     5 #include <mutex>
     6 /*
     7 网络游戏服务器
     8 创建两个线程,一个线程收集玩家命令(用一个数字代表玩家命令),并把命令数据写到一个队列中;
     9 另一个线程从队列中取出玩家发送来的命令,解析,然后执行玩家需要的动作。
    10 list:频繁顺序插入和删除数据时效率高;
    11 vector:频繁随机插入和删除数据时效率高;
    12 准备使用成员函数来作为线程函数
    13 */
    14 using namespace std;
    15 class A{
    16 public:
    17     //把收到的消息(玩家命令)让入到一个队列中的线程函数
    18     void InMsgQue(){
    19         for(int i=0;i<100;i++){
    20             cout<<"InMsgQue执行,插入一个元素"<<i<<endl;
    21             my_mutex.lock();
    22             MyQue.push_back(i);//假设i就是命令
    23             my_mutex.unlock();
    24         }
    25 
    26     }
    27     bool outMsgLULProc(int &command){//这样的目的是为了方便后序对command的操作
    28         my_mutex.lock();
    29         if(!MyQue.empty()){
    30             int command = MyQue.front();
    31             MyQue.pop_front();
    32             my_mutex.unlock();//这个unlock特别容易被忘记写
    33             return true;
    34         }
    35         my_mutex.unlock();
    36         return false;
    37     }
    38     //把数据从消息队列中取出来的线程函数
    39     void OutMsgQue(){
    40         int command=0;
    41         for(int i=0;i<100;i++){
    42             bool result = outMsgLULProc(command);
    43             if(result){
    44                 //消息不空
    45                 cout<<"OutMsgQue正在执行,取出一个元素"<<command<<endl;
    46                 //接下来就考虑处理这个取出来的数据command.........
    47             }
    48             else{
    49                 cout << "OutMsgQue执行,但是消息队列为空"<<i << endl;
    50             }
    51 
    52         }
    53         cout<<"end"<<endl;
    54     }
    55 
    56 private:
    57     list<int> MyQue;
    58     std::mutex my_mutex;//创建一个互斥量
    59 };
    60 
    61 int main(){
    62     A myobj;
    63     std::thread myout(&A::OutMsgQue,&myobj);
    64     std::thread myin(&A::InMsgQue,&myobj);//必须市引用,才能保证线程用的是同一个对象
    65     myout.join();
    66     myin.join();
    67     cout<<"main end"<<endl;
    68     return 0;
    69 }

    2、std::lock_guard类模板

    为了防止忘记unlock,引入lock_guard,类似于智能指针,他会帮你unlock。

    1 bool outMsgLULProc(int &command){
    2        std::lock_guard<std::mutex> guardd(my_mutex);
    3         if(!MyQue.empty()){
    4             int command = MyQue.front();
    5             MyQue.pop_front();
    6             return true;
    7         }
    8         return false;
    9     }

    lock_guard构造函数执行了lock;析构函数执行了unlock。

    只有return的时候才unlock,没有分开使用lock和unlock灵活。

    可以用大括号提前结束guard:

     1     void InMsgQue(){
     2         for(int i=0;i<100;i++){
     3             cout<<"InMsgQue执行,插入一个元素"<<i<<endl;
     4             {
     5                 std::lock_guard<std::mutex> sbguard(my_mutex);
     6                 MyQue.push_back(i);//假设i就是命令
     7             }//大括号结束,lock_guard析构函数执行
     8             //其他处理代码、、、、、、、
     9         }
    10 
    11     }

    三、死锁

    1、演示

    举例:张三在北京等李四,不挪窝;李四在深圳等张三,不挪窝。

    c++中:

    死锁问题是有至少两个锁头也就是两个互斥量才会产生。

    有两个锁:金锁,银锁

    线程:A 和 B

    线程A先锁金锁,金锁锁成功,然后去lock银锁。。。

    线程B先锁银锁,银锁还没被锁的话,锁银锁成功了,然后去lock金锁。。。但是金锁被A锁了。。。

    A拿不到银锁,金锁解不开,B去拿金锁,但是金锁被A锁了,所以银锁解不开。

    这时就发生死锁了。AB两个线程就干等着直到永远~~

    2、解决方案

    只要保证两个互斥量的lock顺序一致,就不会发生死锁。

    3、std::lock()函数模板

    用来处理多个互斥量。

    可以一次锁住两个或多个互斥量。

    它不存在由于多个线程中因为锁的顺序问题导致的死锁。

    如果一个锁没锁住,他就会马上解锁之前所有锁上的互斥量,然后在那儿等着,等所有的互斥量都能够锁住,它才能往下走。

    要么都锁住,要么都没有锁住。

     1     void InMsgQue(){
     2         for(int i=0;i<100;i++){
     3             cout<<"InMsgQue执行,插入一个元素"<<i<<endl;
     4             std::lock(my_mutex1,my_mutex2);//相当于两个互斥量都调用了lock
     5             MyQue.push_back(i);//假设i就是命令
     6             my_mutex1.unlock();
     7             my_mutex2.unlock();
     8             //其他处理代码、、、、、、、
     9         }
    10 
    11     }

    容易忘记unlock

    4、std::lock_guard的std::adopt_lock参数

    使用lock模板,还是容易忘记unlock,那么可是使用lock_guard的adopt_lock参数:

    1     void InMsgQue(){
    2         for(int i=0;i<100;i++){
    3             cout<<"InMsgQue执行,插入一个元素"<<i<<endl;
             std::lock(my_mutex1,my_mutex2);
    4 std::lock_guard<std::mutex> sbguard(my_mutex1,std::adopt_lock); 5 std::lock_guard<std::mutex> sbguard2(my_mutex2,std::adopt_lock); 6 MyQue.push_back(i);//假设i就是命令 7 //其他处理代码、、、、、、、 8 } 9 }

    这个参数就是起一个标记作用,就是如果互斥量已经被lock了,那就不会再调用lock_guard的构造函数了,就只剩析构函数了

  • 相关阅读:
    Vuex的使用
    vue的props属性,vue的插槽
    ES6 Promise对象
    ES6 Map对象以及Set对象
    函数作用域以及块级作用域
    组件之间的传值-$refs&$parent
    Vue中父子组件的传值
    v-on 以及v-model的修饰符以及vue的常用指令
    时间线
    readline和xreadline的区别
  • 原文地址:https://www.cnblogs.com/pacino12134/p/11232607.html
Copyright © 2020-2023  润新知