Skip to content

线程活性问题

死锁活锁和饥饿是破坏系统活跃性的主要三种问题.

死锁

多个线程在执行过程中,因争夺资源而造成的一种互相等待的现象。若无外力干涉,它们都将无法推进

死锁的存在有四大必要条件 ——

  • 互斥 (Mutual Exclusion): 资源在同一时刻只能被一个线程占用。
  • 占有并等待 (Hold and Wait): 线程在持有至少一个资源的同时,请求获取其他被占用的资源。
  • 非抢占 (No Preemption): 资源不能被强制从占用它的线程中剥夺,只能由占有它的线程主动释放。
  • 循环等待 (Circular Wait): 存在一个线程等待环路,如 T1 等待 T2 占用的资源,T2 等待 T1 占用的资源。

在发生死锁的时候,大部分情况我们无能为力.所以更重要的是设计出不会发生死锁的代码 —— 对发生死锁的四大条件下手

  1. 破坏循环等待 —— 强制全局锁排序 —— 强制系统中的所有线程按照固定的、统一的顺序获取锁(例: 始终先加锁标识符小的,再加锁标识符大的)
  2. 使用定时锁

双缓冲

(思想很不错,所以再说说,在图形那块保证没有画面撕裂也是使用类似的思想)

维护了两个 Buffer块 A 和 B, 同时指定 A 为”只读“; B 为“只写”. 用这样的操作把读写操作解耦.在旧的模型中,数据只有一份,所以在读取数据的时候需要加锁;在写入数据的时候也需要加锁;就可能出现死锁问题.现在完全分离 —— 保证不会出现生产者和消费者抢夺同一资源的问题.

只读内存块 A 表示当前最新的数据,不妨定义其为t时刻的数据,那么同一时间,我们让只写内存块B被刷入数据,同时记作t+1时刻的数据(未来人这块).

然后在t时刻结束的时候, 我们强制等待B块的数据也写入完毕,此时只需要交换A和B的地址,就可以使得t+1时刻的数据作为下一帧的最新用来读取的数据

**边界约束:** 双缓冲架构**仅**解决“读-写 (Reader-Writer)”死锁与冲突。在 B 块(Write View)内部,如果存在多个逻辑线程并发写入,**仍需**配合数据分片(Partitioning)或无锁队列,否则 B 块内部依然会发生“写-写 (Writer-Writer)”数据竞争。

活锁

活锁发生时,多个并发线程并没有被操作系统挂起(阻塞),它们仍在不断地执行代码、响应其他线程的状态变化,并尝试推进任务。然而,由于所有涉及的线程都在执行绝对对称的重试逻辑,导致它们的状态不断发生碰撞和回退。系统作为一个整体,有效吞吐量(Goodput)降为零。

看到比较形象的例子 —— 狭路,迎面有人,此时双方都在左右避让(正常运行),但是怎么移动都刚好碰到一起(无法成功获得资源)而无法往前继续行进.

特征也非常明显 ——

  • CPU 占用率极高: 陷入活锁的线程通常处于极速的自旋循环(Spin Loop)中,单核或多核占用率飙升至 100%。
  • 状态高频震荡: 线程内部的状态机在“尝试获取资源”与“释放资源并退避”之间高频切换。
  • 无确定性进展: 与死锁的绝对静止不同,活锁是动态的,但在宏观上任务毫无进度。

所以,解决活锁的核心思想是打破对称性 (Break Symmetry)降低争抢频率 (Reduce Contention)

(实际上感觉都是利用引入随机性等来打破对称)

比如截断二进制指数退避, 当一个线程第c次尝试 CAS 失败时,它不能立刻重试,而是必须等待一段随机时间.(通过随机性来引入非对称性,同时这个随机性随时间指数增长)

饥饿

饥饿是指一个处于就绪状态的线程,因为所需资源的分配策略不公平,导致其等待时间无限期延长,甚至永远无法进入临界区执行。

简单来说,就是线程一直等待执行,但是由于优先级不够高,一直处于等待调度器分配等过程中而无法进入执行.

可以通过优化调度器算法进行解决 —— 比如不是单纯的看优先级,也要结合线程发起的时间戳来进行判断.

或者使用公平锁 / 比较公平的调度方式 —— 但是可能会降低效率.

综上所述

异常类型 (Liveness Hazard)宏观表现 (Macro View)微观 CPU 状态 (Micro View)核心成因 (Core Cause)破局核心哲学 (Resolution Philosophy)
死锁 (Deadlock)永久性卡死,系统/子系统彻底失去响应。极低 (≈ 0%)。线程被操作系统挂起进入 Sleep 状态。满足 Coffman 四条件(互斥、占有并等待、不可剥夺、循环等待)。全局秩序与物理隔离



1. 强制锁排序 (std::scoped_lock)



2. 双缓冲读写隔离



3. 数据分片消除写竞争
活锁 (Livelock)任务吞吐量降为 0,但系统依然在疯狂运转(如风扇狂转)。极高 (≈ 100%)。陷入极速的自旋循环与缓存一致性风暴。多线程执行了高度对称的重试与回退逻辑,步伐完全一致地互相踩踏。打破对称性与硬件降温



1. 引入随机指数退避 (Random Backoff)



2. 插入硬件暂停指令 (_mm_pause)



3. 混合降级调度 (Spin-to-Yield)
饥饿 (Starvation)系统整体运行良好,但极少数特定任务永远无法被执行,产生极长的尾部延迟。正常。霸占资源的线程在正常消耗 CPU。调度器优先级反转、非公平锁的竞争、或读写锁的“偏向性”压制吞吐量与公平性的权衡



1. 引入老化机制 (Aging) 动态提权



2. 使用公平锁 (Ticket Lock/MCS)



3. 实现写者优先 (Writer-Preferring) 机制

Released under the MIT License.