Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

线程与并发

复习

  • 时间片与优先级调度:让多个程序都感觉自己被及时响应
  • 多级反馈队列:不知道任务长短时,靠反馈自动调整优先级
  • 多核调度:任务分到多个 CPU 后还要处理负载和缓存亲和

TL;DR

  • 线程是进程内部的一条执行线,一个进程可以有多条
  • 同一进程的线程共享地址空间和资源,各自有寄存器和栈
  • 并发是“看起来同时”,并行是“真的同时”
  • 线程更轻、切换更省,但共享内存也让它们更容易互相干扰

正文

  一个文本编辑器,既要响应你的按键,又要在后台自动保存文件。它是怎么“同时”做两件事的?

  你可能会说:开两个进程不就行了。可进程之间是隔离的——共享一段数据要费不少周折,创建和切换的开销也不小。于是,人们想在同一个进程内部,分出几条并行的执行线。

进程里的多条执行线

  这个“执行线”,就是线程(thread)。

  一个进程可以包含多个线程。它们共享同一个地址空间、同一批打开的文件、同样的全局数据;但每个线程有自己独立的寄存器和栈——因为每条执行线都要记住自己跑到哪了。

进程
 ├── 地址空间、打开的文件(共享)
 ├── 线程 1:寄存器 + 栈
 ├── 线程 2:寄存器 + 栈
 └── 线程 3:寄存器 + 栈

  好比一间办公室:办公桌、文件柜、打印机(地址空间、资源)是所有员工(线程)共用的;但每个人手头正在做的事(寄存器、栈)各不相同。

并发与并行

  这里要分清两个容易混的词:

  • 并发(concurrency):多个任务在一段时间内都在推进,看起来像同时,但可能是在一个核上快速切换出来的
  • 并行(parallelism):多个任务在同一时刻真的各占一个核,同时执行

  一个核也能“并发”——靠时间片切换,你几乎察觉不到;但真正的“并行”,需要多个核。二者不是一回事。

线程的利与弊

  线程的好处很直接:

  • 轻量:创建、切换只涉及寄存器和栈,比整个进程便宜
  • 方便共享:同一进程的线程天然共享数据,不必像进程那样绕来绕去
  • 响应好:一个线程在等 I/O 时,别的线程可以继续干活

  可共享是一把双刃剑。正因为线程共用同一片内存,它们很容易互相踩脚:两个线程同时改同一个变量,可能算错;一个线程崩溃,整个进程都可能遭殃。

  这正是“并发”话题的由来:一旦允许多条执行线共享数据,我们就得想办法让它们不冲突。这部分内容,从下一章起会连着讲好几章。

思考题

  一个进程的多个线程共享地址空间,只在寄存器和栈上各自独立。为什么“栈”必须每个线程一份,而不能共享?

小结

知识点

  • 线程是进程内的执行线,一个进程可有多个
  • 线程共享地址空间和资源,各有寄存器和栈
  • 并发是“看起来同时”,并行是“真的同时”
  • 线程轻量、便于共享,但共享也带来冲突风险

参考资料

  1. Wikipedia(zh):线程:线程的概念
  2. Wikipedia(zh):并发计算:并发与并行的区别
  3. Wikipedia(zh):并行计算:并行计算

思考题答案(仅供参考)

  栈记录的是“这条执行线自己的调用过程”:函数调到哪里、局部变量是什么、返回地址在哪。每条执行线走的函数、想返回的地方都不一样,必须各存一份,否则两条线会互相覆盖对方的返回地址,一返回就跳错地方。而堆、全局数据这类“大家都要用”的内容,才适合共享。所以“共享地址空间、各自独立栈”的分工,恰好对应了“资源共用、执行独立”。

协议

  本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

封面图

设计师 | 南国微雪