@HarshitNay80531: 在上一篇文章中,我介绍了操作系统基础知识,获得了良好的反响,甚至有来自 @amazon 和 @lyft 的员工私信我讨论相关内容,继…

X AI KOLs Timeline 新闻

摘要

本文解释了推理工程中的并发与线程,涵盖多线程、并行化和竞争条件,并配有视觉示例。

在上一篇文章中,我介绍了操作系统基础知识,获得了良好的反响,甚至有来自 @amazon 和 @lyft 的员工私信我讨论相关内容。本文继续这一话题,在推理工程中,我将介绍如何使进程多线程化并引入并发 https://t.co/FWIRE5SeKI
查看原文
查看缓存全文

缓存时间: 2026/09/15 17:50

在上一篇文章中,我探讨了操作系统基础,并获得了不错的反响——有不少在亚马逊和 Lyft 工作的同行私信交流。基于此,这篇文章将继续深入推论工程的主题,讲解如何实现进程的多线程化以及引入并发机制。https://t.co/FWIRE5SeKI


推论工程:并发与线程 | 第二部分

上一篇文章中我们介绍了进程(即运行中的程序)的概念。正如进程是程序的抽象,线程则是进程的抽象。二者的唯一区别在于:进程拥有独立的地址空间,而线程则共享同一地址空间

Harshit Nayan@HarshitNay80531·Sep 13
因此我决定学习推论工程,并从基础开始。研究了操作系统如何通过进程抽象程序。文章《推论工程:操作系统基础 | 第一部分》中提到:计算机同时运行多个进程(如浏览器、Spotify、Codex)。核心问题是操作系统如何让有限资源(如 CPU)显得无限,或者说如何提供…96557033K

我们可以将经典进程视为单线程进程,但实际上进程可以是多线程的。下面通过图示来直观理解:左侧是包含单个线程的经典进程,右侧是包含 T1 和 T2 两个线程的多线程进程。

单线程与多线程地址空间

从图中可以看到,地址空间中的栈数量取决于进程中的线程数。右侧图示中,栈和堆可以独立增长,只有当地址空间完全耗尽时才会出现问题。但在多线程进程中,你会发现线程2(T2)的可用空间取决于线程1(T1)。这正是栈溢出经典错误的成因——当可用栈空间完全耗尽时就会发生。

那么为什么需要多线程?为什么不使用简单的单线程进程?因为多线程能提供:

  • 并行处理能力
  • 非阻塞程序

并行与并发容易混淆,因为它们非常相似。下面通过两幅图示来解释:

  • 并发:假设只有一个 CPU:任意时刻实际上只有一个线程在运行,但 CPU 在线程间快速切换。

并发

并发

  • 并行:拥有两个 CPU:线程 A 和线程 B 可以真正同时执行。

并行

并行

等一下。根据我们所学,多线程共享同一地址空间,对吧?那么在上面的例子中,线程 A 和线程 B 将访问相同的地址空间。由于它们可以同时执行,当它们同时更新共享变量时会发生什么?这正是问题变得复杂的地方。

举个简单例子:假设有一个共享变量 counter = 0,两个线程都希望将其递增 5 次。

线程1:counter = counter + 1(执行5次) 线程2:counter = counter + 1(执行5次)

我们期望最终值为 10,但实际结果未必如此。原因是 counter = counter + 1 并非原子操作,它实际包含三个步骤:

读取 counter 值 加 1 写入新值

例如,当 counter = 5 时:线程1读取到 5,但在写入 6 之前,操作系统切换到了线程2。

T1:读取 5 T2:读取 5 T2:写入 6 T1:写入 6

两个线程都执行了递增操作,但最终结果仅为 6 而非预期的 7

这就是竞态条件。执行结果取决于线程的调度时序。

问题的关键不在于计算机的随机性,而在于时序不确定性。当多个线程运行时,我们无法准确预测操作系统何时会切换线程。指令本身是确定性的,但并发线程执行这些指令的顺序可能变化

如何解决这个问题?
要解决此问题,首先需要理解根本原因:线程共享内存状态(参考第一张图示)。

访问共享内存状态的代码部分称为临界区。我们需要确保同一时刻只有一个线程执行该区域,这就是互斥机制。

但如何确保同一时刻只有一个线程进入临界区?最直接的想法是:“检查是否有其他线程已在其中,若无则进入。”但这存在缺陷——检查操作本身可能被中断。想象两个线程同时执行:

T1:检查 → 无人进入 T2:检查 → 无人进入 T1:进入临界区 T2:进入临界区

现在两个线程都进入了临界区,问题依旧存在。因此我们需要一种不可被中途打断的操作——即原子操作(字面意为“全有或全无”的操作)。这是同步机制的基本思想。常见解决方案是使用锁(lock)

于是执行过程变为:

原流程: T1:读取 → 修改 → 写入 T2:读取 → 修改 → 写入

优化后:

T1:加锁 → 读取 → 修改 → 写入 → 解锁 T2:等待 → 加锁 → 读取 → 修改 → 写入 → 解锁

锁机制如何实现?
如果获取锁的过程不是原子操作,我们可能会遭遇试图解决的相同竞态条件。在硬件层面,CPU 提供特殊的原子操作,允许我们在没有其他线程干扰的情况下检查和修改值。

目前无需深究具体实现细节,核心思想是:硬件为操作系统提供了构建安全锁的基础模块

至此,我们建立了完整的概念模型:线程→支持并发与并行→共享状态引发同步问题→竞态条件→临界区→互斥→原子性→同步机制。

相似文章

@injaneity: https://x.com/injaneity/status/2075659478096376158

X AI KOLs Timeline

本文解释了批处理和并行操作如何改善AI计算机使用系统中的延迟和效率,重点介绍了pi-computer-use和cua-driver等开源实现,它们在Codex出现类似功能之前就取得了显著的性能提升。