Ceph 撸源码系列(二):Ceph源代码里的那些锁 std::mutex(2 of 3)
一、前言:
Nautilus v14.2.4 里有一个Performance PR msg/async: avoid put message within write_lock #20731 ,这个PR主要是把 for 循环里的m->put()的代码放到锁之外,来减少临界区里的代码,以提高performance。
注:这个PR里由于要把put()代码分开,新增了Message *数组,用来指向std::list 类型的sent消息。所以新版本里把m->put()替换成了pending[k]->put()
二、分析这个PR
1. 先过一下这个PR的diff 代码
2. 先分析上面PR的关键代码:
删除了 std::lock_guard<std::mutex> l(write_lock)
增加了write_lock.lock(); write_lock.unlock();
到提问题的环节了,什么是 std::lock_guard?write_lock变量定义在哪?
1)什么是 std::lock_guard?
互斥类的最重要成员函数是lock()和unlock()。在进入临界区时,执行lock()加锁操作,如果这时已经被其它线程锁住,则当前线程在此排队等待。退出临界区时,执行unlock()解锁操作。
更好的办法是采用”资源分配时初始化”(RAII)方法来加锁、解锁,这避免了在临界区中因为抛出异常或return等操作导致没有解锁就退出的问题。它极大地简化了程序员编写mutex相关的异常处理代码。C++11的标准库中提供了std::lock_guard类模板做mutex的RAII。
std::lock_guard对象并不负责管理mutex对象的生命周期,lock_guard对象只是简化了mutex对象的上锁和解锁操作,方便线程对互斥量上锁,即在某个lock_guard对象的生命周期内,它所管理的锁对象会一直保持上锁状态;而lock_guard的生命周期结束之后,它所管理的锁对象会被解锁。程序员可以非常方便地使用lock_guard,而不用担心异常安全问题。
小结:在std::lock_guard对象构造时,传入的mutex对象(即它所管理的mutex对象)会被当前线程锁住。在lock_guard对象被析构时,它所管理的mutex对象会自动解锁,不需要程序员手动调用lock和unlock对mutex进行上锁和解锁操作。
详细介绍参考:C++锁的管理-- std::lock_guard和std::unique_lock
2)write_lock变量定义在哪
write_lock其实就是AsyncConnection类里的一个 std::mutex类型 成员变量
//AsyncConnection.h文件里/* * AsyncConnection maintains a logic session between two endpoints. In other * word, a pair of addresses can find the only AsyncConnection. AsyncConnection * will handle with network fault or read/write transactions. If one file * descriptor broken, AsyncConnection will maintain the message queue and * sequence, try to reconnect peer endpoint. */class AsyncConnection : public Connection {...std::mutex write_lock;...}
3. 分析整个PR
1) PR版本前的代码
void AsyncConnection::handle_ack(uint64_t seq){ ldout(async_msgr->cct, 15) << __func__ << " got ack seq " << seq << dendl; // trim sent list std::lock_guard<std::mutex> l(write_lock); while (!sent.empty() && sent.front()->get_seq() <= seq) { Message* m = sent.front(); sent.pop_front(); ldout(async_msgr->cct, 10) << __func__ << " got ack seq " << seq << " >= " << m->get_seq() << " on " << m << " " << *m << dendl; m->put(); }}
由于一进入AsyncConnection::handle_ack()函数,就定义了 std::lock_guard<std::mutex> l(write_lock),std::lock_guard构造函数里就调用了write_lock.lock加锁,一直到函数结束才释放锁。
2) PR版本后的代码:
void AsyncConnection::handle_ack(uint64_t seq){ ldout(async_msgr->cct, 15) << __func__ << " got ack seq " << seq << dendl; // trim sent list static const int max_pending = 128; int i = 0; Message *pending[max_pending]; write_lock.lock(); while (!sent.empty() && sent.front()->get_seq() <= seq && i < max_pending) { Message* m = sent.front(); sent.pop_front(); pending[i++] = m; ldout(async_msgr->cct, 10) << __func__ << " got ack seq " << seq << " >= " << m->get_seq() << " on " << m << " " << *m << dendl; } write_lock.unlock(); for (int k = 0; k < i; k++) pending[k]->put();}
去掉了std::lock_guard<std::mutex> l(write_lock)代码,使用了纯手工write_lock.lock()/write_lock.unlock()来指定临界区区间。把之前的m->put()放到了临界区之外,以此来提高performance。这个属于代码级别的性能优化。
三、总结
std::lock_guard<std::mutex> l(write_lock):由于锁lock的生命周期在整个std::lock_guard<std::mutex>变量里有效,所以它的生命周期控制精准度相比write_lock.lock()/write_lock.unlock()低。但std::lock_guard<std::mutex>避免了在临界区中因为抛出异常或return等操作导致没有解锁就退出的问题,这是其优点。
write_lock.lock()/write_lock.unlock():其锁lock的生命周期控制精准度相比std::lock_guard<std::mutex>高,所以在CEPH源代码里看到了write_lock.lock()/write_lock.unlock() 和 std::lock_guard<std::mutex> 的共存。它们用于各自的场景。
Ceph 撸源码系列(二):Ceph源代码里的那些锁 std::mutex(2 of 3)相关推荐
- 带着问题撸源码系列-zookeeper-临时节点[ephemeral]是怎么弄的?我写了一堆临时节点为啥我一掉线就全没了?
问题 带着问题撸源码系列-zookeeper-临时节点[ephemeral]是怎么弄的?我写了一堆临时节点为啥我一掉线就全没了? 猜测 可能是有线程维护着,每个session有一个临时节点列表,一旦客 ...
- PostgreSQL源码系列二:Postgres-XL调试基础GDB Debug范例
一. 前言 接上一篇,上篇的pg_hba的配置还是有点问题的,本篇将通过Debug来解析问题根源. 二. 问题 由于postgres是超级管理账户,实际使用中不可能开放给普通用户,我们模拟下生产环境, ...
- 源码解读_入口开始解读Vue源码系列(二)——new Vue 的故事
作者:muwoo 转发链接:https://github.com/muwoo/blogs/blob/master/src/Vue/2.md 目录 入口开始解读Vue源码系列(一)--造物创世 入口开始 ...
- Spring源码系列(十二)Spring创建Bean的过程(二)
1.写在前面 上篇博客主要Spring在创建Bean的时候,第一次调用的Bean的后置处理器的过程,同时笔者也打算将整个Spring创建的Bean的过程,通过这个系列,将Bean的创建过程给讲清楚,废 ...
- 高效阅读嵌入式源码系列一:静态分析神器understand软件基本操作
系列文章目录 高效阅读嵌入式源码系列一:静态分析神器understand软件基本操作 高效阅读嵌入式源码系列二:understand阅读linux.uboot等源码 高效阅读嵌入式源码系列三:unde ...
- adreno源码系列(五)打开kgsl
// 接"adreno源码系列(二)kgsl driver"中第3.3节 static int kgsl_open_device(struct kgsl_device *devic ...
- 二、ceph编译源码、单机搭建调试环境
2019独角兽企业重金招聘Python工程师标准>>> 准备一台机器: root@test3:~# cat /proc/version Linux version 3.13.0-32 ...
- ceph bluestore源码分析:非对齐写逻辑
文章目录 环境 原理说明 总结 环境 ceph:12.2.1 场景:ec 2+1 部署cephfs,执行如右写模式:dd if=/dev/zero of=/xxx/cephfs bs=6K count ...
- 使用rpmbuild对ceph的源码包进行重新打包
进入ceph源码包下载ceph相关的rpm包和tar包 我们下载的是ceph-12.1.1-0.el7.src.rpmceph L版本的rpm包 执行命令rpmbuild --rebuild ceph ...
- Spring源码系列:依赖注入(二)createBean
在Spring源码系列:依赖注入(一)(AbstractBeanFactory-getBean)最后说道getBean是依赖注入的起点,bean的创建都是通过createBean来完成具体的创建的.c ...
最新文章
- html 资源缓存,解决index.html缓存问题
- 基于workerman实现的web消息推送站内信功能
- oracle可以使用提交完成的事务,【体系结构】Oracle数据提交与事务隔离实验 oradebug挂起lgwr进程...
- Android中使用SeekBar拖动条实现改变图片透明度
- problem about can't trigger exit-command in CR
- HTTP协议实体的基本讲解
- Android开发之运行客户的Demo拿不到数据
- java中,如何实现输入一个正整数,并将这个数字反转输出,比如输入123,输出321
- Kibana 的安装(Windows版本)新手入门
- 如何查找历史线程阻塞原因_吊打面试官!Java多线程并发 108 道题,你能答对多少?...
- 文字生成视频,只需一步(附论文下载)
- 对俄罗斯应用“一刀切”,乌克兰知名开发商推出 Mac 专用反间谍软件
- 解决log4j.properties不起作用的问题
- 用QFileDialog::getOpenFileUrls打开本地文件:指定路径
- SecureCRT右键粘贴的设置
- oracle删除导入库,oracle数据库删除和导入方法
- Oracle查看表空间及使用情况
- Javascript分割/截取/连接字符串
- Matlab fftshift and ifftshift and some confusions
- 计算机文档字体替换和加重号,word加重号怎么加
热门文章
- angular 倒计时
- 成员函数指针有多态的效果吗?
- python如何处理文本文件_python如何选择合适的异常处理方式?
- MongoDB实战-面向文档的数据(找到最合适的数据建模方式)
- CentOS6.5 firefox安装flash插件
- 【Android UI设计与开发】第04期:引导界面(四)仿人人网V5.9.2最新版引导界面...
- AgileEAS.NET平台开发实例-药店系统-视频教程系列-索引
- 当调用wcf, 小心返回值包含enum越界的错误。
- SAP-PP后台配置(第二部分)
- mysql存储过程的学习(mysql提高执行效率之进阶过程)