一句话:"SMP = 多核对等共享内存;共享数据必须同步;缓存一致性和伪共享是多核的坑;关中断只关当前核,多核要自旋锁。"
SMP(对称多处理)= 多个处理器核心共享同一块物理内存、同一个操作系统、完全对等。每个核有自己的寄存器和缓存(L1/L2),问题出在"共享数据 + 各自缓存"的冲突上。
| 并行 | 并发 | |
|---|---|---|
| 定义 | 同一时刻同时执行多个 | 同一时刻只执行一个,轮流切换 |
| 需要 | 多核/多CPU | 单核即可 |
| 例子 | 4 核跑 4 线程 | 单核跑多线程 |
| 数据竞争 | 多核共享数据必出 | 单核也要注意(中断/调度) |
| SMP | AMP(非对称多处理) | |
|---|---|---|
| 核的关系 | 对等(对称) | 不等(异构分工) |
| 内存 | 共享同一块 | 可独立/私有 |
| OS | 一个 OS 管理所有核 | 可多个/专用核 |
| 例子 | 普通多核 PC/服务器 CPU | 大小核 big.LITTLE、GPU+CPU |
| 缓存一致性 | 原子性 | 伪共享 |
|---|---|---|
| 核间缓存与内存不一致 | 操作不可分割 | 不同变量同缓存行互相干扰 |
| 靠缓存一致性协议(MESI) | 靠原子指令/锁 | 靠缓存行对齐/填充 |
MESI 协议(四状态):Modified 已改 / Exclusive 独占 / Shared 共享 / Invalid 失效。核改数据 → 标记 Modified,广播让其他核缓存失效(Invalid)→ 其他核再读时从内存/该核同步最新值。
伪共享判断:变量地址间隔 < 缓存行大小(通常 64 字节)→ 可能伪共享。如 int arr[8](32 字节 < 64)中 arr[0] 和 arr[1] 同缓存行;arr[0] 和 arr[100](400 字节 > 64)绝不会。
数据竞争:多核 count++ 是"读→改→写"三步,同时执行被切开 → 丢失更新(A 读 0、B 读 0,都写 1,结果 1 不是 2)。
| 原子操作 | 锁(互斥锁) | 自旋锁 | |
|---|---|---|---|
| 实现 | 硬件原子指令(CAS/test-and-set) | 基于原子指令 + 阻塞/睡眠 | 忙等轮询 |
| 开销 | 最小 | 有上下文切换 | 忙等耗 CPU |
| 适用 | 单变量简单操作(count++) | 多步骤临界区 | 多核短临界区 |
| 是否阻塞 | 否 | 是(睡眠) | 忙等 |
锁本身就是用原子指令实现的。原子操作不阻塞别人,锁会阻塞等待。
count++(共享变量)不加锁,说"不同核不会同时写,安全" → 错,count++ 读改写被切开,丢失更新(结果 1 不是 2)arr[0] 和 arr[100] 说"离得远没伪共享" → 对(400 字节 > 64 缓存行,不可能同缓存行);但 arr[0] 和 arr[1](4 字节 < 64)→ 会伪共享| 相邻概念 | 关系 | 孤立理解会犯的错 |
|---|---|---|
| 临界区(主题27) | 单核关中断即可;多核必须自旋锁/原子 | 以为关中断多核也有效 |
| 原子操作(主题21) | 多核共享单变量的保护手段 | 以为原子操作要阻塞别人 |
| 进程 vs 线程 | 多核多线程真并行,共享数据竞争更易触发 | 以为多核不用加锁 |
| 优先级反转(主题26) | 多核自旋锁也有优先级反转问题 | 以为自旋锁没开销 |
2026-08-26
每主题一页,复习时只翻本目录。