前面实现了一个 带值变更通知能力的字典类(线程不安全),童鞋们有没有发现演示代码使用了 lock语法糖, 这个有没有问题呢?

没背景说个铲铲

同程艺龙基础架构部推出的数据获取组件DAL.Connection,我们要做到在切换连接配置时清空数据库连接池, 这就涉及到切换连接的时候,触发变更通知。

•.NET 如何清空连接池?•面试官:实现一个带值变更通知能力的Dictionary

仔细阅读《面试官:实现一个带值变更通知能力的Dictionary》一文的童靴们有没有发现一个细节:我使用了lock语法糖无脑加锁。

这里面有个前置知识点:C# Dictionary线程不安全。
什么叫线程不安全,请看这个✍️ 你管这叫"线程安全"?

这在高并发下会有问题:大多数时候下DBA并不会变更业务方的数据库连接,这是一个多读少写的场景, 我们无脑使用lock在多数时间会人为阻塞请求。

到这个时候,我们就要想到读写锁ReaderWriterLockSlim

宝藏好物:ReaderWriterLockSlim

Use ReaderWriterLockSlim to protect a resource that is read by multiple threads and written to by one thread at a time. ReaderWriterLockSlim allows multiple threads to be in read mode, allows one thread to be in write mode with exclusive ownership of the lock, and allows one thread that has read access to be in upgradeable read mode, from which the thread can upgrade to write mode without having to relinquish its read access to the resource.

简而言之:

ReaderWriterLockSlim提供对某资源在某时刻下的多线程同读 或 单线程独占写。
此外,ReaderWriterLockSlim还提供从读模式无缝升级到独占写模式。

总结下来:

读写锁处于以下四种状态:

1.未进入: 没有线程进入锁(或者所有线程退出锁)2.读模式:每次调用EnterReadlock时,锁计数都会增加,但允许您读取其中的代码块。3.写模式:独占、排他4.可升级的读模式(upgradeable read mode):多线程读,其中一个线程具备在某时刻升级到排他写模式的可能。

btw,读写锁相比常规lock之外,还具备锁超时的机制,能避免未知原因持续占有锁导致的死锁。

这就很适合我们开发DAL.Connection组件的多读少写的场景。

微软ReaderWriterLockSlim页面还很贴心的给了一个基于读写锁的缓存操作封装类SynchronizedCache

开箱即用的缓存操作类

基于ReaderWriterLockSlim对线程不安全的Dictionary进行了包装, 可以作为一个多读少写的缓存操作类。

public class SynchronizedCache
{private ReaderWriterLockSlim cacheLock = new ReaderWriterLockSlim();private Dictionary<int, string> innerCache = new Dictionary<int, string>();public int Count{ get { return innerCache.Count; } }public string Read(int key){cacheLock.EnterReadLock();try{return innerCache[key];}finally{cacheLock.ExitReadLock();}}public void Add(int key, string value){cacheLock.EnterWriteLock();try{innerCache.Add(key, value);}finally{cacheLock.ExitWriteLock();}}public bool AddWithTimeout(int key, string value, int timeout){if (cacheLock.TryEnterWriteLock(timeout)){try{innerCache.Add(key, value);}finally{cacheLock.ExitWriteLock();}return true;}else{return false;}}public AddOrUpdateStatus AddOrUpdate(int key, string value){cacheLock.EnterUpgradeableReadLock();try{string result = null;if (innerCache.TryGetValue(key, out result)){if (result == value){return AddOrUpdateStatus.Unchanged;}else{cacheLock.EnterWriteLock();try{innerCache[key] = value;}finally{cacheLock.ExitWriteLock();}return AddOrUpdateStatus.Updated;}}else{cacheLock.EnterWriteLock();try{innerCache.Add(key, value);}finally{cacheLock.ExitWriteLock();}return AddOrUpdateStatus.Added;}}finally{cacheLock.ExitUpgradeableReadLock();}}public void Delete(int key){cacheLock.EnterWriteLock();try{innerCache.Remove(key);}finally{cacheLock.ExitWriteLock();}}public enum AddOrUpdateStatus{Added,Updated,Unchanged};~SynchronizedCache(){if (cacheLock != null) cacheLock.Dispose();}
}

缓存操作类SynchronizedCache每次操作会返回操作结果,和常见的字典一样,不带值变更通知的能力,我们还是像《面试官:实现一个带值变更通知能力的Dictionary》 一文那样,添加值变更事件,注册变更逻辑。

public event EventHandler<ValueChangedEventArgs<string>> OnValueChanged;//--- 节选自AddOrUpdate方法
cacheLock.EnterWriteLock();
try
{OnValueChanged?.Invoke(this, new ValueChangedEventArgs<string>(key));innerCache[key] = value;
}
finally
{cacheLock.ExitWriteLock();
}
return AddOrUpdateStatus.Updated;//---if (sc.AddOrUpdate(key, value) == SynchronizedCache.AddOrUpdateStatus.Updated)
{Console.WriteLine($"已经发生了值变更,原key对应的键值已经被重写。");}
}

输出旁白

本文记录了读写锁在日常开发中的实践,大多数场景都是多读少写,读者可以思考一下是不是也可以将项目中的无脑lock替换为SynchronizedCache

本文是同程艺龙DAL.Connection组件研发过程的一个小插曲,有心的读者可以往上翻一翻,了解上下文背景、了解小码甲的思考过程。

这就像我们高中做数学题,直接看答案并不能快速提升,结合上下文自然、流畅的转到这个方向才是最重要的。

最后,觉得有用,一键三连,激浊扬清❤️。

目前100000+人已关注加入我们

面试官: 平时开发中你用过读写锁吗?相关推荐

  1. 这些HTML、CSS知识点,面试和平时开发都需要 No10-No11

    系列知识点汇总 1.基础篇 这些HTML.CSS知识点,面试和平时开发都需要 No1-No4(知识点:HTML.CSS.盒子模型.内容布局) 这些HTML.CSS知识点,面试和平时开发都需要 No5- ...

  2. 面试官:Vue中组件和插件有什么区别?

    一.组件是什么 回顾一下对组件的定义: 组件就是把图形.非图形的各种逻辑均抽象为一个统一的概念(组件)来实现开发的模式,在Vue中每一个.vue文件都可以视为一个组件 组件的优势 降低整个系统的耦合度 ...

  3. 【Redis系列】面试官:Redis中的数据已经过期,为什么还占用这内存?

    如果有面试官问Redis中的数据已经过期为什么还占用这内存? 它是因为Redis本身的过期策略和缓存淘汰机制所导致的. 说说Redis的过期策略和缓存淘汰机制 先来说说Redis的过期策略,Redis ...

  4. 面试官:Redis中集合数据类型的内部实现方式是什么?

    虽然已经是阳春三月,但骑着共享单车骑了这么远,还有有点冷的.我搓了搓的被冻的麻木的手,对着前台的小姐姐说:"您好,我是来面试的."小姐姐问:"您好,您叫什么名字?&quo ...

  5. 【性能优化】面试官:Java中的对象和数组都是在堆上分配的吗?

    写在前面 从开始学习Java的时候,我们就接触了这样一种观点:Java中的对象是在堆上创建的,对象的引用是放在栈里的,那这个观点就真的是正确的吗?如果是正确的,那么,面试官为啥会问:"Jav ...

  6. 面试官:说说Innodb中LRU怎么做的?

    引言 某日,小编去面试(纯属瞎编),有了如下对话 面试官:"懂mysql吧,知道CPU在读硬盘上数据的时候,是怎么解决CPU和硬盘速度不一致问题么?"我:"懂啊,mysq ...

  7. 面试官:InnoDB中一棵B+树可以存放多少行数据?

    点击上方"朱小厮的博客",选择"设为星标" 后台回复"书",获取 来源:r6a.cn/fUA9 InnoDB一棵B+树可以存放多少行数据?这 ...

  8. 面试官:项目中最大的风险是什么?

    风险意识是衡量测试人员质量能力的一个重要因素,项目中的风险点往往是出现缺陷概率比较大的地方,也是产品质量最薄弱点.不管是向老板汇报项目情况.还是求职面试,对于测试来说,风险意识很容易被谈到.例如老板会 ...

  9. 【245期】面试官:同类中两个方法加同步锁,多个线程支持同时访问这两个方法吗?...

    点击上方"Java精选",选择"设为星标" 别问别人为什么,多问自己凭什么! 下方有惊喜,留言必回,有问必答! 每天 08:15 更新文章,每天进步一点点... ...

最新文章

  1. 综合布线的12大热点技术
  2. Kettle使用_8 存储过程结合获取系统信息
  3. ios调用restful接口_Postman调用https异常解决
  4. cytoscape插件下载_Cytoscape下载
  5. php中orm模型,模型model
  6. 基于物品的协同过滤推荐算法_《推荐系统实践》3.基于物品的协同过滤算法
  7. .netcore下的微服务、容器、运维、自动化发布
  8. 前端学习(1810):前端调试之css装饰cursor
  9. 【Unity】关于屏幕自适应的思路
  10. extjs 获取id的值_Extjs combox获取显示值和ID值
  11. DXUT框架剖析(14)
  12. 利用VBB仿真——实现24小时电子钟
  13. 数值计算实验1 Matlab基础实验
  14. 程序员也要学英语——连词、并列句和从句
  15. Matlab实现给黑白图片上色
  16. Flutter Sliver滚动组件
  17. 配置java comp路径_java:comp/env/jdbc/ 的两种配置方法
  18. # 汉洛塔问题的解决思路及其代码
  19. mysql自学英语差怎么_英语太差怎么办?
  20. Java 集合深入理解(10):Deque 双端队列

热门文章

  1. 更新整理本人所有博文中提供的代码与工具(C++,2013.11)
  2. 2d访问冲突_Light | 基于环形分隔微镜阵列的高速随机访问轴向聚焦系统
  3. 如何在Apache环境下配置Rewrite规则
  4. 68.iOS设备尺寸及型号代码(iPhoneXR/XS)
  5. Python 项目实践三(Web应用程序)第四篇
  6. 算法(第4版)Robert Sedgewick 刷题 第一章(1)
  7. JCheckbox全选
  8. 日记2015.11.5
  9. matlab练习程序(PCASVD)
  10. 最佳新秀Java(22)——再次了解泛型