五互斥量概念用法死锁

Posted pacino12134

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了五互斥量概念用法死锁相关的知识,希望对你有一定的参考价值。

一、互斥量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的构造函数了,就只剩析构函数了

以上是关于五互斥量概念用法死锁的主要内容,如果未能解决你的问题,请参考以下文章

C++ 多线程学习笔记:互斥量概念和用法死锁演示及解决

LINXU多线程(进程与线程区别,互斥同步)

线程同步与互斥详解

C++ 11 互斥量与死锁

Linux线程安全

Linux多线程——互斥和同步