6. 并发控制:互斥 (1)

背景回顾:虽然 “线程库” 入门简单,但多处理器、编译优化、缓存和动态流水线迫使我们 “放弃” 许多顺序程序编程时的基本假设,给理解、编写并发程序带来了困难。

本讲内容:人类从来不害怕困难。我们通过创造出新的手段,帮助我们编写正确的并发程序:

  • 互斥问题和 Peterson 算法
  • 原子指令和自旋锁

6.1 阻止并发 (并行) 的发生

6.2 使用 load/store 实现互斥

Peterson 算法比那些 “玩具” 的例子复杂得多,“随时随地” 都有发生线程切换的可能性。这些可能性对于手工枚举来说,已经有些太过琐碎了——这也是在很长一段时间里,大家给出错误互斥算法的原因。

对于 “到底哪一个 barrier 是不可缺少” 这个问题已经超出了本门课程的讨论范围。我们试图用这个例子向大家传递一个思想:除非你对多处理器并发有足够的理解,请不要自作主张写聪明的并发程序。在 99.9% 的情况下,你都可以在不触及此类底层行为的前提下优化你的代码。

6.3 在多处理器上实现互斥

终于,我们借助计算机硬件提供的短时原子性实现了多处理器间的互斥。

Take-away Messages

并发编程 “很难”:想要完全理解并发程序的行为,是非常困难的——我们甚至可以利用一个 “万能” 的调度器去帮助我们求解 NP-完全问题。因此,人类应对这种复杂性的方法就是退回到不并发。通过互斥实现 stop/resume the world,我们就可以使并发程序的执行变得更容易理解——而只要程序中 “能并行” 的部分足够多,串行化一小部分也并不会对性能带来致命的影响。

课后习题/编程作业

📚阅读材料

教科书 Operating Systems: Three Easy Pieces 上次课的阅读材料:

  • 第 25 章 - Dialogue on Concurrency
  • 第 26 章 - Concurrency and Threads
  • 第 27 章 - Thread API