线程的前世今生
进程
早期的计算机只有一个单核 CPU, 操作系统把进程作为 CPU 调度单元. 进程拥有独立的内存地址空间, 那时候还没有线程的概念.
进程有 3 个状态, 分别是阻塞, 就绪, 运行. 当进程所需资源未到位时是阻塞状态, 当进程拥有资源但未被 CPU 调度是就绪状态, 当进程用有资源并且被 CPU 调度了就是运行状态.
用户态线程
随着程序越来越复杂, 调度产生的上下文切换也越发昂贵, 于是程序员寻思能不能在同一地址空间 (Address Space) 下, 执行多个进程. 但操作系统内核出于保护目的, 禁止一个进程直接访问另一个进程的地址空间.
既然操作系统不支持, 程序员决定在用户空间下维护一张线程表, 实现可以自行调度的 "进程", 这就是用户态线程, 现在一般叫做纤程或协程.
用户态线程的优势有:
在用户空间下进行线程切换的速度远快于在操作系统内核中的实现
程序员可以自行实现垃圾回收器来回收线程
当线程数量过多时, 由于在用户空间维护线程表, 不会占用大量的操作系统空间
用户态线程的劣势有:
由于操作系统不知道线程的存在, 因此当一个进程中的某一个线程进行系统调用时, 比如缺页中断而导致线程阻塞, 此时操作系统会阻塞整个进程, 即使这个进程中其它线程还在工作
假如进程中一个线程长时间不释放 CPU, 因为用户空间并没有时钟中断机制, 会导致此进程中的其它线程得不到 CPU 而持续等待
内核态线程
伴随多核 CPU 的出现, 操作系统也开始支持线程了. 内核空间维护了一张线程表, 线程由内核调度, 这就是内核态线程, 也称作轻量进程(Light Weight Process,LWP). 今天说的线程, 如非指定, 都是内核线程.
内核态线程的优势有:
当一个线程阻塞时, 操作系统可以选择将 CPU 交给同一进程中的其它线程, 或是其它进程中的线程, 而对于用户态线程的调度只能在本进程中执行, 直到操作系统剥夺了当前进程的 CPU
内核态线程的劣势有:
所有可能阻塞线程的调用都是 System Call, 相比在用户空间下造成线程阻塞的 System runtime call, 成本高出不少
创建和销毁的成本更高. 为了降低成本, 当一个线程需要销毁时, 仅作标记销毁; 当需要新建一个线程时, 就复用被标记销毁的线程即可
接收系统信号的单位是进程, 而不是线程, 那么由进程中的哪一个线程接收系统信号呢
逻辑核心
"单核 CPU 在同一时间内只能够运行一个进程", 在过去很长一段时间是对的, 直至同时多线程 (SMT) 的出现. 超标量处理器 (1 个 CPU 物理核心) 可以同时跑超过一个线程, 例如 Intel 的 hyperthreading 支持一个物理核心跑两个线程. 讲几核的 CPU 的核心数一般指物理核心数, 如果是指逻辑核心数一般直接会说线程数, 几个逻辑核心就同时支持几套架构状态和几个线程.
"单进程多线程可以同时用到 CPU 的双核心", 在主流操作系统上没问题. 对操作系统内核而言, 直接可见的 CPU 资源是逻辑核心, 可以调度的任务一般直接实现成 CPU 支持的线程, 所以 单核 CPU 可以同时跑多个任务.
不同操作系统中任务的概念不一样:
Windows 调度的是线程, 进程只是线程的容器
Linux 内核 不区分进程或者线程, 调度的任务在用户空间可以被实现成一个进程或进程中的线程.
参考
操作系统中的进程与线程
多线程可以同时使用 CPU 的多个核心? https://www.v2ex.com/t/285551
来源: http://www.bubuko.com/infodetail-3424399.html