@HarshitNay80531: 在上一篇文章中,我介绍了操作系统基础知识,获得了良好的反响,甚至有来自 @amazon 和 @lyft 的员工私信我讨论相关内容,继…
摘要
本文解释了推理工程中的并发与线程,涵盖多线程、并行化和竞争条件,并配有视觉示例。
查看缓存全文
缓存时间: 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 提供特殊的原子操作,允许我们在没有其他线程干扰的情况下检查和修改值。
目前无需深究具体实现细节,核心思想是:硬件为操作系统提供了构建安全锁的基础模块。
至此,我们建立了完整的概念模型:线程→支持并发与并行→共享状态引发同步问题→竞态条件→临界区→互斥→原子性→同步机制。
相似文章
@suraj_sharma14:如果你想精通推理工程,这个资源是一座金矿:https://github.com/elizabetht/10…
一个GitHub仓库,提供结构化的100天LLM推理工程学习计划,涵盖从CUDA内核到自动扩展的主题,并配有可运行的脚本。
@injaneity: https://x.com/injaneity/status/2075659478096376158
本文解释了批处理和并行操作如何改善AI计算机使用系统中的延迟和效率,重点介绍了pi-computer-use和cua-driver等开源实现,它们在Codex出现类似功能之前就取得了显著的性能提升。
@jino_rohit:在过去的6到8个月里,我一直尝试转向机器学习系统和AI基础设施领域。以下是我最喜欢的一些…
作者分享了他们在过去6-8个月里在ML系统和AI基础设施方面的工作,包括一个轻量级的Python LLM推理引擎(tachyon),在消费级硬件上通过连续批处理和前缀缓存实现了每秒600+ tokens,还有关于CUDA/CUTE DSL和集体通信的博客文章,以及对SGLang和vLLM的贡献。
@ariG23498: 我一直很钦佩 @stevhliu 的工作。我认为他的技术文章是最好的之一。在最新的……
一个系列帖文介绍了 Hugging Face 的 transformers 库如何高效加载模型,涵盖 meta device、safetensors、CUDA 缓存等内容。
@akshay_pachaar: https://x.com/akshay_pachaar/status/2094765529231929361
本文是一篇实践指南,介绍如何为代理工作运行本地AI模型,重点讨论硬件权衡,并引入Magnitude,一个开源推理服务器,它简化配置以优化性能。