• 互斥量(mutex)的基本概念
  • 互斥量的用法
    • lock(), unlock()
    • std::lock_guard类模板
  • 死锁  
    • 死锁演示
    • 死锁的一般解决方案
    • std::lock()函数模板
    • std::lock_guard的std::adopt_lock参数

互斥量(mutex)的基本概念

线程保护共享数据,操作时,某个线程用代码把共享数据锁住,其他想操作共享数据的线程必须等待;等待解锁,其他线程才能继续操作共享数据。

1)锁定住

2)操作

3)解锁

“互斥量”的基本概念,互斥量是个类对象,理解成一把锁,多个线程尝试用lock()成员函数来加锁这个锁头,只有一个线程能锁定成功,只要返回了表示锁成功了,如果没锁成功,那么流程就会卡在Lock()这里不断尝试去锁,如果永远不成功就“死锁”。

互斥量的用法

首先,使用互斥量需要引入一个头

#include <mutex>

lock(), unlock()

 线程先lock(),操作共享数据,然后操作完之后,unlock();

lock()和unlock()要成对使用,有lock()必然要有unlock,每调用一次lock(),必然应该调用一次unlock()。不应该出现一次lock(),两次unlock(),非对称的数据调用必然会出错!!!lock和unlock用来保护代码,由程序开发者决定,明确决定从哪里保护到哪。

class ProcessRequest {
public:
//把命令加入到一个队列
void inMsgRecvQueue() { for (int i = 0; i < 100000; ++i) {
my_mutex.lock();
cout << "插入一个元素" << endl;
m_msgRecvQueue.push_back(i); //假设这个队列表示玩家的命令
my_mutex.unlock();
} //占用时间片
} bool outMsgLULProc(int &command) {
if (!m_msgRecvQueue.empty()) {
int command = m_msgRecvQueue.front();
m_msgRecvQueue.pop_front();
return true;
}
return false;
}
//把命令移出一个队列
void outMsgRecvQueue() {
int command = 0;
for (int i = 0; i < 100000; ++i) {
my_mutex.lock();
bool result = outMsgLULProc(command);
if (result == true) {
cout << "outMsgRecvQueue() 执行,取出一个元素" << endl;
}
else
{
cout << "outMsgRecvQueue() 还执行,但是消息队列为空" << endl;
//消息队列为空
}
my_mutex.unlock();
//占用时间片
}
} private:
std::list<int> m_msgRecvQueue; //容器,用于表示玩家的发送过来命令
std::mutex my_mutex;
};

  

std::lock_guard类模板

为了防止忘记写unlock,std::lock_guard,可以自动unlock,功能类似unique_ptr。类模板可以直接取代lock和unlock,同时取代两个,也就是说用了lock_guard之后不能使用lock和unlock了。

lock_guard构造函数里执行了 mutex::lock(),在析构函数里执行了mutex::unlock()。超出作用域析构,不用担心结果。

死锁

在操作系统中,进程互相竞争资源,但是资源有限,都在等待占用资源,运行完毕释放资源。C++中,至少两个锁才会产生死锁问题,一把锁不会产生死锁。多线程以及多进程改善了系统资源的利用率并提高了系统 的处理能力。然而,并发执行也带来了新的问题——死锁。

死锁是指两个或两个以上的进程(线程)在运行过程中因争夺资源而造成的一种僵局(Deadly-Embrace) ) ,若无外力作用,这些进程(线程)都将无法向前推进。

某计算机系统中只有一台打印机和一台输入设备,进程P1正占用输入设备,同时又提出使用打印机的请求,但此时打印机正被进程P2 所占用,而P2在未释放打印机之前,又提出请求使用正被P1占用着的输入设备。这样两个进程相互无休止地等待下去,均无法继续执行,此时两个进程陷入死锁状态。

关于死锁的一些结论:

a)参与死锁的进程数至少为两个
b)参与死锁的所有进程均等待资源
c)参与死锁的进程至少有两个已经占有资源
d)死锁进程是系统中当前进程集合的一个子集
e)死锁会浪费大量系统资源,甚至导致系统崩溃

死锁

两个线程A和B:

a)A执行的时候,先锁金锁,金锁lock()成功,程序往下走,然后lock()银锁,还没锁成功,此时因为线程调度,上下文切换,一不小心,线程A切换走了。

b)线程B得到了执行的权限,先锁银锁,然后线程B要去lock金锁,此时此刻,死锁产生了。

分析:线程A,拿着金锁,等银锁。线程B,拿着银锁,等金锁。此时,两个线程流程都走不下去,所以后面的代码有解锁金锁的解不开,线程B拿不到金锁,流程走不下去,银锁解不开。

死锁的一般解决方案

只要调用顺序保持一致,就不会死锁。

Processor1{
mutex1.lock();
mutex2.lock();
//
....
// mutex1.unlock();
mutex2.unlock();
} Processor2{
mutex1.lock();
mutex2.lock();
//
...
//
mutex1.unlock();
mutex2.unlock();
}

  

std::lock()函数模板

std::lock_guard的std::adopt_lock参数

std::adopt_lock是个结构体对象,起一个标记的作用:这个互斥量已经lock(),不需要std::lock_guard<std:mutex> 构造函数里面再面对Mutex对象进行lock()。

参考文献

https://blog.csdn.net/wljliujuan/article/details/79614019

最新文章

  1. 机器指令翻译成 JavaScript —— 终极目标
  2. 关于chrome控制台那些事
  3. MyEclipse中代码格式化后自动换行
  4. lphp输出控制output controll(header, ob_xxx)
  5. CentOS 6.5 x86_64系统手动释放内存
  6. PHP判断字符串中是否包含指定字符串,支持中文哦
  7. Android中this、super的区别
  8. SAM4E单片机之旅——15、触屏输入与SPI通信
  9. iphone设置铃声
  10. Spring Data Jpa配置
  11. 项目任务管理(TaskMgr)设计篇
  12. 【转】 为SeekBar滑块设置固定值以及自定义Seekbar,progressbar样式--不错
  13. Linux crontab定时器设置(定期执行java程序)(转)
  14. Python--抽象类接口类
  15. Docker镜像配置redis集群
  16. 正则化 L1 L2
  17. 多条件搜索优化sql
  18. vue-devtools : vue的调试工具及log显示工具 vconsole
  19. sql server 只读帐号设置能读取存储过程,view等内容。
  20. 浅析JS中的堆内存与栈内存

热门文章

  1. Ethical Hacking Tutorials
  2. CSS3 &amp; gradient &amp; color &amp; background
  3. Event Bus &amp; Event Emitter
  4. BGV等 DeFi产品暴涨背后隐藏着什么?
  5. 【Python】面向对象:类与继承简单示例
  6. MySQL -- 内部临时表
  7. js合并字符串的3种方法和效率
  8. 渗透测试--Nmap主机识别
  9. CentOS 7.7+ Python3.7 下安装virtualenv和virtualenvwrapper
  10. Git:分支管理