系统性能调优必知必会
陶辉
智链达CTO、前阿里云高级技术专家
立即订阅
4378 人已学习
课程目录
已更新 8 讲 / 共 34 讲
0/4登录后,你可以任选4讲全文学习。
开篇词 (1讲)
开篇词 | 万变不离其宗,性能优化也有章可循
免费
基础设施优化 (6讲)
01 | CPU缓存:怎样写代码能够让CPU执行得更快?
02 | 内存池:如何提升内存分配的效率?
03 | 索引:如何用哈希表管理亿级对象?
04 | 零拷贝:如何高效地传输文件?
05 | 协程:如何快速地实现高并发服务?
06 | 锁:如何根据业务场景选择合适的锁?
系统层网络优化 (1讲)
07 | 性能好,效率高的一对多通讯该如何实现?
系统性能调优必知必会
15
15
1.0x
00:00/00:00
登录|注册

06 | 锁:如何根据业务场景选择合适的锁?

陶辉 2020-05-11
你好,我是陶辉。
上一讲我们谈到了实现高并发的不同方案,这一讲我们来谈谈如何根据业务场景选择合适的锁。
我们知道,多线程下为了确保数据不会出错,必须加锁后才能访问共享资源。我们最常用的是互斥锁,然而,还有很多种不同的锁,比如自旋锁、读写锁等等,它们分别适用于不同的场景。
比如高并发场景下,要求每个函数的执行时间必须都足够得短,这样所有请求才能及时得到响应,如果你选择了错误的锁,数万请求同时争抢下,很容易导致大量请求长期取不到锁而处理超时,系统吞吐量始终维持在很低的水平,用户体验非常差,最终“高并发”成了一句空谈。
怎样选择最合适的锁呢?首先我们必须清楚加锁的成本究竟有多大,其次我们要分析业务场景中访问共享资源的方式,最后则要预估并发访问时发生锁冲突的概率。这样,我们才能选对锁,同时实现高并发和高吞吐量这两个目标。
今天,我们就针对不同的应用场景,了解下锁的选择和使用,从而减少锁对高并发性能的影响。

互斥锁与自旋锁:休眠还是“忙等待”?

我们常见的各种锁是有层级的,最底层的两种锁就是互斥锁和自旋锁,其他锁都是基于它们实现的。互斥锁的加锁成本更高,但它在加锁失败时会释放 CPU 给其他线程;自旋锁则刚好相反。
取消
完成
0/1000字
划线
笔记
复制
© 版权归极客邦科技所有,未经许可不得传播售卖。 页面已增加防盗追踪,如有侵权极客邦将依法追究其法律责任。
该试读文章来自付费专栏《系统性能调优必知必会》,如需阅读全部文章,
请订阅文章所属专栏。
立即订阅
登录 后留言

精选留言(14)

  • 忆水寒
    首先要知道,协程是用户态运行的,所以减少内核切换。而自旋锁和互斥锁其实都是有内核控制线程的访问,所以协程肯定不是使用互斥锁Mutex和自旋锁SpinLock实现的。
    我想,协程之间的互相协作肯定也是有一种协商机制,之前在一篇博客看到好像是使用yield实现的,从而共享CPU使用权。
    2020-05-11
    1
    4
  • 那时刻
    Go里的自旋锁需要自己实现,方便协程调度。协程使用自旋锁的时候,这是spinLock 的Lock方法

    for !atomic.CompareAndSwapUint32(sl, 0, 1) {
        runtime.Gosched()
    }

    其中runtime.Gosched,是把阻塞的协程调度出去,这样调度器可以执行其他协程。
    2020-05-12
    3
  • 不会C++不改名
    总结下,我的理解 不对的欢迎指出:
    四种IO模型
    同步阻塞: 调用了阻塞的系统调用,内核会将线程置于休眠状态,并进行调度
    同步非阻塞: 调用了非阻塞的系统调用,系统调用会立刻返回结果.通过重试的方式来不断获取结果,直到满足条件
    异步阻塞: 会通过注册回调函数来实现结果的通知,注册完成线程会被内核挂起进入休眠状态
    异步非阻塞: 同上,但注册完成后线程可以继续执行.

    同步: 等待结果的返回才能进行下一步操作
    异步:不一直等待结果的返回,而是通过向IO调度框架注册回调函数的方式来进行通知.通过框架来实现
    阻塞: 不会立刻返回结果,此时线程会被内核挂起,让出cpu
    非阻塞: 立刻返回结果
    异步非阻塞放在一起用才能起到并发的作用.
    同步非阻塞也可以实现并发.
    同步与异步是针对编程方式的
    阻塞与非阻塞的因为系统调用的实现方式导致的

    协程: 将异步的注册回调函数以及非阻塞的系统调用来封装成一个阻塞的协程,即将等待回调时通过线程中的上下文切换来实现线程的无感知切换,感觉有点类似于异步阻塞,但阻塞是用户态的,也就是是由用户态来进行线程的虚拟的休眠(通过线程上下文的切换)


    两种基本锁
    自旋锁 : 通过CAS函数来实现,将观察锁的状态与获取锁合并为一个硬件级的指令,通过在用户态来观察锁的状态,并进行获取锁,来避免因获取锁失败导致的线程休眠.获取锁失败会忙等待,即过一段时间在去获取锁(通过循环实现等待时间),通过pause指令来减少循环等待时的耗电量.

    互斥锁: 一种独占锁,获取失败的线程会被内核置为休眠状态.

    其他锁都是通过这两种锁实现的
    读写锁
    读优先锁
    写优先锁

    乐观锁:乐观锁并没有加锁,而是通过执行完成后的版本号对比来实现
    2020-05-12
    6
    1
  • test
    协程有些操作是使用线程池来实现的,需要加锁
    2020-05-11
    1
  • 陈政璋
    老师你好,文中开头提到必须弄清加锁成本以及锁发生概率,有没有可以量化的方法或者工具呢?
    2020-05-11
    1
  • 小一日一
    当并发访问共享资源,冲突概率非常低的时候,可以选择无锁编程。

    多低算非常低?能否给出个经验值?谢谢老师
    2020-05-14
  • carol
    看的很带劲,就好像吃了个美妙的西瓜。
    2020-05-13
  • 刘耳总
    在Python里的协程await db call 这种应该算是异步阻塞吧?
    2020-05-13
  • 阿鼎
    请问老师
    1,Windows下的临界区,先自旋,再互斥,这是最佳实践吗?
    2,无锁队列,虽然队列本身使用cas,但队列的压入事件是不是还需要条件变量+锁。
    2020-05-13
  • myrfy
    “自旋锁开销少,在多核系统下一般不会主动产生线程切换,很适合异步、协程等在用户态切换请求的编程方式,有助于高并发服务充分利用多颗 CPU。“

    这段描述是不是不太准确?协程在用户态由协程框架调度,自旋锁只会阻塞当前协程,导致其他协程不能获得执行权。

    协程框架下的sleep等函数,都是框架提供的而不是操作系统提供的,其工作原理是将当前协程的唤醒时间告知协程框架调度器后主动让出当前协程,让其他协程有机会运行,当每一个协程发生主动切换时,协程框架会检测是否有等待唤醒的协程,若有则会按照一定的策略唤醒睡眠的协程。
    2020-05-12
  • 小喵喵
    老师,锁为什么分类那么多,是从哪些维度来分的,比如从数据库来分,可以分为悲观和乐观锁,以及为什么这么分类呢?
    2020-05-12
  • 不会C++不改名
    老师文中提到自旋锁通过在用户态来实现观察锁加锁解锁的操作,感觉协程中的锁可以通过CAS函数来实现,如果获取锁失败则进行协程的切换.
    2020-05-12
  • 孙志强
    一直以为CAS是乐观锁
    2020-05-11
    2
  • 安排
    在线程竞争不激烈的情况下乐观锁cas效率才会高,而且cas存在ABA问题,不过类似文中的CAS(lock, 0, pid)如果保证pid唯一,则不存在ABA问题。
    请教一下老师,这里的pid代表的这个数可以是任意宽度的吗?我看有的解决ABA问题的方案是加版本号,这个具体怎么实现呢?有没有demo参考?
    2020-05-11
    1
收起评论
14
返回
顶部