读写锁应用浅谈

9 篇文章 0 订阅
4 篇文章 0 订阅

后端开发常常碰到多线程同步问题,也就会经常用到锁,其中关于读写锁,大家还争论不休,主要是很多人会倾向于使用读写锁,认为读写锁能比普通的互斥锁能带来性能的提升,这里先把陈硕大佬总结的贴出来吧(摘自LINUX 多线程服务端编程,第二章,第三节,P23页),大佬说的已经非常全面到位了:

=============================================================================================

=============================================================================================

    读写锁(Readers-Writer lock, 简写为rwlock) 是个看上去很美的抽象,它明确区分了read 和 write 两种行为。

    初学者常干的一件事情是,一见到某个共享数据结构频繁读而很少写,就把mutex替换为rwlock。甚至首选rwlock来保护共享状态,这不见得是正确的。

    从正确性方面来说,一种典型的易犯错误是在持有read lock的时候修改了共享数据。这通常发生在程序的维护阶段,为了增加新功能,程序员不小心在原来read lock保护的函数中调用了会修改状态的函数。这种错误的后果跟无保护并发读写共享数据是一样的。

    从性能方面说,读写锁不见得比普通mutex更高效,无论如何reader lock 加锁的开销不会比mutex lock小,因为它需要更新当前reader的数目。如果临界区很小,锁竞争不激烈,那么mutex往往会更快。见$1.9的例子。

    reader lock 可能允许提升(upgrade)为writer lock, 也可能不允许提升。考虑$2.1.1的post()和traverse()示例,如果用读写锁保护foos对象,那么post() 应该持有读写锁,而traverse() 应该持有读锁。如果允许吧读锁提升为写锁,后果跟使用recursive mutex一样,会造成迭代器失效,程序崩溃。如果不允许提升, 后果跟使用non-recursive mutex一样,会造成死锁。我宁愿程序死锁,留个“全尸”好查验。

    通常reader lock 是可重入的,writer lock是不可重入的。但是为了防止writer饥饿,writer lock 通常会阻塞后来的reader lock,因此reader lock在重入的时候可能死锁。另外,在追求低延迟读取的场合也不适用读写锁。

===========================================================================================

===========================================================================================

这里我也总结下:

1,读写锁一定比互斥锁(普通锁)单次加锁开销大,很容易理解,读写锁要做额外的引用计数已加锁读写性质判别,也做了benchmark,macbook pro i5 8g上,读写锁20ns,普通锁13ns左右

2,读写锁容易误用,例如加了读锁结果进行了写操作

3,读写锁的优势在于可以读并发,那么如果加锁粒度控制的比较小,那么读写锁依然么有优势,如果加锁粒度比较大,那就是设计问题了。。

4,读写锁重入会死锁,比如读锁重入过程中被写锁抢占,就很容易死锁。

5,读写锁的非对称设计会造成延时的抖动,特殊场景不可接受。

6,读写锁的优势场景是  大量的读,读并发,多读少写,https://blog.csdn.net/myz123321/article/details/89048002  像博文中做的实验,上面总结的第一点就解释了,单纯比较rw锁和互斥锁,没有场景的比较没有意义,读写锁必定会慢,读写锁必须用在读并发且大量读少量写的场景,大量读,读并发,多读少写,这三个条件缺一不可,不然就没必要用读写锁,互斥锁就好,具体解释下就是:

a:读并发+大量读,但是没有多读少写,比如读写差不多甚至读比写还少,这个场景显然不需要读写锁,这个是最明显的,因为读写差不多就得互斥锁上了。

b:读并发+多读少写,但是量不大,偶尔才并发一下就没必要,量不大的时候读写锁带来的误用,死锁,性能开销不如用互斥锁,而且这里隐含的场景是读的时间比较集中才会造成读并发但量不大,除了场景原因也有设计问题。

c:大量读+多读少写,即使这种情况,如果大量的读都是分散开的,没有并发读,或者读的量和锁的粒度还没有上升到容易并发读的程度,那么也没必要读写锁,读写锁的优势完全没发挥出来。

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Redisson提供了的功能。在并发场景中非常有用,特别是对于操作远多于操作的情况。允许多个线程同时持有,但只允许一个线程持有。 通过使用Redisson的,你可以实现对某个关键字进行并发的操作控制。当一个线程持有时,其他线程也可以持有,但是当有线程持有时,其他的都会被阻塞。 在实际应用中,如果有一个场景需要并发取数据,然后并发进行扣减操作,可能会出现扣减失败的情况。这是因为在取的时候还有库存,但是在扣减的时候库存已经没有了。为了避免这种情况,可以使用Redisson的来保证并发的线程安全性。 在使用Redisson的时,可以通过以下方式进行操作: - 使用`readLock()`方法获取,并通过`lock()`方法加,然后使用`unlock()`方法释放。 - 使用`writeLock()`方法获取,并通过`lock()`方法加,然后使用`unlock()`方法释放。 在你的代码示例中,可以看到使用Redisson的的具体操作。通过`getReadWriteLock(key)`方法获取,然后使用`readLock()`方法获取或使用`writeLock()`方法获取。最后使用`lock()`方法加,使用`unlock()`方法释放。 总结起来,Redisson的提供了一种并发控制机制,可以在并发场景下确保数据的一致性与线程安全性。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [redission解决db和缓存双不一致](https://blog.csdn.net/weixin_43944305/article/details/120191365)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值