1.1 我是一个线程

沿线程的创建、就绪、运行、等待与回收追踪共享资源和独立上下文,用可重放实验定位并发故障。

学习目标

  • 能解释线程与进程的归属关系,并沿创建、就绪、运行、等待、结束追踪上下文变化
  • 能用共享计数器和调度事件说明读—改—写为何不是天然的原子操作
  • 能在正常、边界和故障场景中保存首个偏离、资源所有者、恢复动作与复位证据

1.1 我是一个线程

本页依据刘欣《码农翻身》(2018 年第 1 版)及出版社公开书目信息,独立重构 1.1 我是一个线程。正文、代码、图示、实验和练习都是本课程重新设计的教学材料,不复制原书正文、插图、练习答案或代码。

线程不是一条脱离系统的故事线,而是进程资源合同内可调度的执行上下文。线程共享进程的地址空间和部分资源,却拥有自己的调用栈、寄存器状态、线程 ID 和调度状态;调度器可以在可运行线程之间切换,但切换不自动替共享数据建立同步协议。

三个会让线程模型失真的陷阱

五个目录节点到机制证据

1.1 我是一个线程

不是把人物换成线程名,而是固定一个可观察的执行单位:它属于哪个进程、当前拿到什么 CPU 时间、是否访问共享状态,以及结束时谁负责回收。

线程的独立性只覆盖执行上下文,不自动覆盖数据所有权。读者应先为一个线程写出输入、所属进程和预期轨迹,再改变一个调度或同步条件,比较最早变化的节点。

初生牛犊

对应创建线程的机制证据。创建成功至少要记录线程 ID、入口参数、所属进程、栈和初始状态;只看到一个返回值,不能证明它已经执行。

如果创建后线程没有进入就绪队列,复核应停在创建到就绪的首个缺口,而不是把后续没有输出归咎于业务逻辑。

渐入佳境

对应就绪与运行的转换。调度器保存和恢复线程上下文,但不会替线程决定共享变量的业务不变量;获得 CPU 只证明执行机会存在。

记录就绪时间、运行时间片、上下文切换和写回顺序,才能把“没有运行”与“运行了但被覆盖”区分开。

虎口脱险

对应等待资源的机制证据。等待不是失败,也不是自动完成同步;需要保存等待原因、资源所有者、唤醒条件和重新入队事件。

若线程持锁等待另一个持锁线程,首个不满足的资源依赖就是诊断入口。先解除或回滚依赖,再用同一输入重放,不能只增加超时掩盖死锁。

江湖再见

对应结束与回收。线程返回不等于共享队列、锁、文件和上下文已经可释放;join、取消和异常路径都必须有明确的所有者。

把退出码、未完成任务、等待者、资源引用和回收时间放在同一条轨迹中,才能确认结束是可重放的终态。

线程生命周期:执行上下文始终属于一个进程CPU 时间片改变运行状态,不会自动保护共享状态1创建ID + 栈状态证据2就绪等待 CPU状态证据3运行上下文当前执行点4等待锁 / I/O资源边界5回收所有权状态证据线程结束不等于共享队列、锁和最后引用已经可以释放
专属图示:把线程的执行状态、资源边界和回收责任放进同一条轨迹。

线程执行合同

共享计数器的理想结果可以写成:

counterfinal=counter0+i=1nΔicounter_{final}=counter_0+\sum_{i=1}^{n}\Delta_i

但这个式子只描述目标,不保证并发实现正确。每次增量都应绑定线程 ID、读取版本、调度事件和同步证据;若两个线程读取同一版本后分别写回,必须记录冲突,而不是用最终数字猜测历史。

state = baseline(thread_id, process_id, counter=0)
for event in scenario:
    state = transition(state, event)
    assert owner_and_invariant_hold(state)
assert reset(state) == baseline(thread_id, process_id, counter=0)

这段草图的边界是验证轨迹,不是一个可直接替代线程库的实现。实际复核需要保存输入、线程上下文、状态转换、同步协议、首差、最终结果和复位结果。

五步复核线程生命周期

分步1 / 5

1. 创建线程并固定所有权

冻结进程 ID、线程入口、参数、栈、共享变量和初始状态。先预测线程何时进入就绪队列,以及创建失败时哪些资源必须回收。

线程生命周期:执行上下文始终属于一个进程CPU 时间片改变运行状态,不会自动保护共享状态1创建ID + 栈状态证据2就绪等待 CPU状态证据3运行上下文当前执行点4等待锁 / I/O资源边界5回收所有权状态证据线程结束不等于共享队列、锁和最后引用已经可以释放
专属图示:把线程的执行状态、资源边界和回收责任放进同一条轨迹。

Lab

线程交错与回收实验

只改变一个调度或同步条件,观察线程状态、共享版本和所有权判定。

T1 获得 CPU,原子增量后结束

T1: read v0 → add 1 → write v1 → join → reclaim

判定

通过:所有者明确,计数和引用闭合

当前场景:基线运行;记录线程 ID、进程归属、调度顺序、共享版本和回收事件。

正常、边界与故障证据

线程证据矩阵:最终数字不能替代交错历史正常看推进,边界看等待,故障看首个合同违约观察项正常边界故障归属进程明确共享边界越权调度按序推进等待饥饿状态版本递增交错丢失更新回收引用归零延迟悬挂引用线程 ID、调度顺序、同步协议和引用记录共同解释结果
专属图示:从线程归属到共享状态,再到最后引用的四类验收证据。
样本只改变的变量预期判定必存证据
正常一个线程完成增量并由所有者回收状态按合同推进,资源闭合线程 ID、上下文、版本、回收记录
边界时间片、队列容量、锁等待或任务数接近阈值在资源边界停住并给出唤醒或回退阈值、等待者、所有者、替代路径
故障一次读—改—写交错或一次回收动作失败首个偏离可定位,修复后可重放首差、调度顺序、同步证据、复位结果

故障诊断:先找最早的合同违约

  1. 确认归属:核对线程 ID、进程 ID、栈、共享资源和当前状态;跨进程或已结束线程的访问先暂停。
  2. 确认交错:按时间顺序排列读取、计算、写回、锁、等待、唤醒和切换;找第一处不满足同步协议的事件。
  3. 确认恢复:决定是补锁或原子操作、解除资源依赖、取消线程还是延迟回收;保留修复前后的同一输入。
  4. 确认终态:检查计数版本、队列、锁、文件、等待者和最后引用,确保复位后没有把上轮污染带入下一次实验。

术语表

名词解释

本章出现的专业名词,用大白话再讲一遍。

1.1 我是一个线程

在进程资源上下文中拥有独立调用栈、寄存器状态、线程 ID 和调度状态的执行单位。

初生牛犊

线程从创建请求进入可运行集合前后建立身份、入口和初始状态的阶段。

渐入佳境

线程进入可运行队列并获得调度机会,在时间片或主动让出后继续推进的阶段。

虎口脱险

线程因锁、条件、I/O 或其他资源暂时等待,并在条件满足后重新竞争运行机会的阶段。

江湖再见

线程完成、取消或异常退出后,由所有者确认引用并释放资源的结束阶段。

练习

练习

问题 1: 两个线程都把计数器从 0 加到 1,最终结果为 1,最应该先检查什么?

问题 2: 一个线程已经返回,但另一个线程仍持有它使用的共享队列,能否立即释放队列?

问题 3: 线程一直处于等待状态,你如何区分资源耗尽和死锁?

资料与写作方式声明

本章以码农翻身权威目录界定学习范围,并结合正文列出的技术资料独立重写;不宣称复现原书正文,也不沿用原作表述。

原作版权归作者与出版社所有;本站原创教学结构与表述仅供学习交流。

本页小结

线程的关键不是把执行线拟人化,而是能说明它属于哪个进程、何时获得 CPU、为何等待、怎样保护共享状态,以及结束后谁负责回收。完成标准是用同一份输入重放五个生命周期节点,在故障轨迹中定位首个合同违约,修复后证明计数、所有权和复位结果都闭合。

讨论

评论区加载中…