学习操作系统P5 并发控制:互斥 (自旋锁、互斥锁和 futex)

视频:https://www.bilibili.com/video/BV1ja411h7jt/?spm_id_from=333.999.0.0&vd_source=7a1a0bc74158c6993c7355c5490fc600

Peterson算法实现互斥的效率其实很低

如何正确地在多处理器上实现互斥呢?

 

上下两段代码在描述同一个东西,但上面的代码更好理解(前面一张图的图形化解释更好理解,也是在描述同一种互斥协议)

下面的视角比较难理解 

调试并发程序的两个方法:

1. 尽可能详尽的测试

2. 尽可能的证明(model checker)

处理器实现的 “原子指令” 从根本上消灭了并行,它保证两个事情:

1. 每一个针对同一个关键对象的原子指令,在同一时间只有一个被执行,即,它们可以按时间顺序排成 1 2 3 4

2. 每一个原子指令在被执行时,在它之前被执行的原子指令所产生的结果,对于当下这个将要被执行的原子指令都是可见的

由于在C语言中,给变量上把锁实际上是在给内存中的某一个数据上锁,会涉及到总线。

所以实际上是在总线上上锁

当两个CPU都想给内存上锁时,总线会决定谁先上锁、谁要等待。

不过,今天的CPU由于有缓存,内存锁的这个问题会更加复杂,如下图

 

 当两个CPU的内存里都有一个变量m时,如果CPU1要给变量m加锁,那么它必须把其它CPU的缓存里的变量m都给清除掉

所以,今天的 intel 多核CPU,所有core的L1 cache都是互联的(缓存一致性)。

RISCV的一种很神奇的原子性操作的想法:

对关键变量的访问,都可以分成  Load(读) Exec(计算) Store(写) 三个步骤

当我在读取一个内存数据时,我把它读出来,然后在它旁边加上一个标记,称为 Load-Reserved

接着开始做计算 Exec

做完计算后,尝试把新值写入变量,如果此时:

1. 我之前加上的标记被消除了(其它core写入那个变量会消除标记);那么放弃写入,重新开始读写该关键变量。

2. 如果标记没有被消除,说明这段时间没有其它core写入这个变量,可以直接把新值写入该变量

上下这两段代码只是阐明 Compare-and-Swap 的思想

(在计算机科学中,比较交换(CAS)是用于多线程以实现同步的原子指令。 它将存储位置的内容与给定值进行比较,并且只有它们相同时,才将该存储位置的内容修改为新的给定值。 这是作为单个原子操作完成的。)

LR/SC+cas 除了实现原子操作,还可以检测原子操作的拥堵

LR/SC+cas 在失败时,就知道有人在跟自己竞争

0. 正在尝试拿锁的人会不停的触发 lock 指令,引起处理器间的缓存同步

1. 空转

2. 获得自旋锁的线程也可能被操作系统切换出去,造成 100% 资源浪费

有些时候,进行同一个计算任务的线程越多,并不代表效率越高。

原因是刚刚提到的三个 “自旋锁的缺陷” 性能问题

记录了一些 做 System 科研时,做Benchmarks 可能出现的错误

自旋锁真正的使用场景是操作系统内核的并发数据结构

虚拟机关中断并不会真正把中断关掉(不然虚拟机死机会引起物理机死机),采用的是一些其它的机制

想要实现用户态线程的互斥其实很容易

性能优化的常见技巧:看 average(frequent) case 而不是 worst case

在 pthread 中,spinlock 更快的 fastpath。 mutexlock(实际实现方式就是 futex) 更快的 slow path

在撞锁程度较少时,pthread还是使用原子指令;在撞锁程度较高时,pthread会使用睡眠锁

即便 Futex 的设计者,第一次写Futex也是错误的。

所以,不要好高骛远,解决问题时先用简单的方法,不要考虑性能,然后再迭代

善用 model checker 等调试和可视化工具

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值