一、前言:

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)相关推荐

  1. 带着问题撸源码系列-zookeeper-临时节点[ephemeral]是怎么弄的?我写了一堆临时节点为啥我一掉线就全没了?

    问题 带着问题撸源码系列-zookeeper-临时节点[ephemeral]是怎么弄的?我写了一堆临时节点为啥我一掉线就全没了? 猜测 可能是有线程维护着,每个session有一个临时节点列表,一旦客 ...

  2. PostgreSQL源码系列二:Postgres-XL调试基础GDB Debug范例

    一. 前言 接上一篇,上篇的pg_hba的配置还是有点问题的,本篇将通过Debug来解析问题根源. 二. 问题 由于postgres是超级管理账户,实际使用中不可能开放给普通用户,我们模拟下生产环境, ...

  3. 源码解读_入口开始解读Vue源码系列(二)——new Vue 的故事

    作者:muwoo 转发链接:https://github.com/muwoo/blogs/blob/master/src/Vue/2.md 目录 入口开始解读Vue源码系列(一)--造物创世 入口开始 ...

  4. Spring源码系列(十二)Spring创建Bean的过程(二)

    1.写在前面 上篇博客主要Spring在创建Bean的时候,第一次调用的Bean的后置处理器的过程,同时笔者也打算将整个Spring创建的Bean的过程,通过这个系列,将Bean的创建过程给讲清楚,废 ...

  5. 高效阅读嵌入式源码系列一:静态分析神器understand软件基本操作

    系列文章目录 高效阅读嵌入式源码系列一:静态分析神器understand软件基本操作 高效阅读嵌入式源码系列二:understand阅读linux.uboot等源码 高效阅读嵌入式源码系列三:unde ...

  6. adreno源码系列(五)打开kgsl

    // 接"adreno源码系列(二)kgsl driver"中第3.3节 static int kgsl_open_device(struct kgsl_device *devic ...

  7. 二、ceph编译源码、单机搭建调试环境

    2019独角兽企业重金招聘Python工程师标准>>> 准备一台机器: root@test3:~# cat /proc/version Linux version 3.13.0-32 ...

  8. ceph bluestore源码分析:非对齐写逻辑

    文章目录 环境 原理说明 总结 环境 ceph:12.2.1 场景:ec 2+1 部署cephfs,执行如右写模式:dd if=/dev/zero of=/xxx/cephfs bs=6K count ...

  9. 使用rpmbuild对ceph的源码包进行重新打包

    进入ceph源码包下载ceph相关的rpm包和tar包 我们下载的是ceph-12.1.1-0.el7.src.rpmceph L版本的rpm包 执行命令rpmbuild --rebuild ceph ...

  10. Spring源码系列:依赖注入(二)createBean

    在Spring源码系列:依赖注入(一)(AbstractBeanFactory-getBean)最后说道getBean是依赖注入的起点,bean的创建都是通过createBean来完成具体的创建的.c ...

最新文章

  1. html 资源缓存,解决index.html缓存问题
  2. 基于workerman实现的web消息推送站内信功能
  3. oracle可以使用提交完成的事务,【体系结构】Oracle数据提交与事务隔离实验 oradebug挂起lgwr进程...
  4. Android中使用SeekBar拖动条实现改变图片透明度
  5. problem about can't trigger exit-command in CR
  6. HTTP协议实体的基本讲解
  7. Android开发之运行客户的Demo拿不到数据
  8. java中,如何实现输入一个正整数,并将这个数字反转输出,比如输入123,输出321
  9. Kibana 的安装(Windows版本)新手入门
  10. 如何查找历史线程阻塞原因_吊打面试官!Java多线程并发 108 道题,你能答对多少?...
  11. 文字生成视频,只需一步(附论文下载)
  12. 对俄罗斯应用“一刀切”,乌克兰知名开发商推出 Mac 专用反间谍软件
  13. 解决log4j.properties不起作用的问题
  14. 用QFileDialog::getOpenFileUrls打开本地文件:指定路径
  15. SecureCRT右键粘贴的设置
  16. oracle删除导入库,oracle数据库删除和导入方法
  17. Oracle查看表空间及使用情况
  18. Javascript分割/截取/连接字符串
  19. Matlab fftshift and ifftshift and some confusions
  20. 计算机文档字体替换和加重号,word加重号怎么加

热门文章

  1. angular 倒计时
  2. 成员函数指针有多态的效果吗?
  3. python如何处理文本文件_python如何选择合适的异常处理方式?
  4. MongoDB实战-面向文档的数据(找到最合适的数据建模方式)
  5. CentOS6.5 firefox安装flash插件
  6. 【Android UI设计与开发】第04期:引导界面(四)仿人人网V5.9.2最新版引导界面...
  7. AgileEAS.NET平台开发实例-药店系统-视频教程系列-索引
  8. 当调用wcf, 小心返回值包含enum越界的错误。
  9. SAP-PP后台配置(第二部分)
  10. mysql存储过程的学习(mysql提高执行效率之进阶过程)