CMU 15-213 CSAPP:并发编程与同步的艺术(Concurrent Programming)
写在前面这是本系列系统级编程学习笔记的终章。当我们的 Web 服务器终于能够通过网络响应一个客户端之后新的问题接踵而至如果同时有一万个人访问怎么办并发编程Concurrent Programming是现代计算机科学的王冠但它也是无数隐蔽 Bug 的万恶之源。这篇笔记我们将探讨进程、线程、事件驱动的恩怨情仇以及伟大的计算机先驱们是如何利用“信号量”来约束这些狂暴的并发洪流的。Lec 23 Concurrent Programming并发编程是很困难的只要涉及到并发程序的执行顺序就成了一个玄学取决于操作系统的调度策略。常见的灾难包括竞争 (Races):程序的正确性居然依赖于哪个线程跑得快。举例谁能抢到飞机上的最后一个座位死锁 (Deadlock):资源分配不当导致所有人都卡死。举例十字路口四个方向的车互不相让彻底堵死。活锁 / 饥饿 / 公平性 (Livelock / Starvation):某个倒霉的线程因为优先级太低永远得不到 CPU 时间。举例你在排队但总是有 VIP 插队到你前面。Iterative Servers 的基本流 (串行服务器的致命缺陷)上一篇笔记中我们写了一个简单的 Echo Server。它的逻辑是接收一个连接 - 读数据 - 写回数据 - 断开 - 处理下一个。致命缺陷如果 Client 1 连上了但是它去上厕所了迟迟不发数据Server 就会死死阻塞在read上。此时如果有其他几万个 Client 想要连接全都会被拒绝。解决方案必须使用并发服务器 (Concurrent Servers)同时拉起多个逻辑流来服务不同的客户端。书写并发服务器的方法 (The 3 Approaches)如何实现并发业界有三大流派基于进程 (Process-based):内核负责管理每个客户端分配一个独立的进程。基于事件 (Event-based):程序员手工在用户态调度所有客户端都在一个进程/线程里利用 I/O 多路复用如select/epoll。基于线程 (Thread-based):进程和事件的折中方案。内核调度但所有线程共享同一个地址空间。基于进程 (Process-based)Pros (优势):绝对安全。由于每个进程有独立的地址空间一个客户端崩溃绝对不会影响其他客户端。不共享全局变量没有加锁的烦恼。Cons (劣势):太重了进程创建 (fork) 和上下文切换的开销极大。且进程间通信IPC如管道、共享内存编码非常复杂。基于事件 (Event-based)这就是大名鼎鼎的Node.js, Nginx, Redis背后的核心架构Pros (优势):性能怪兽。没有进程/线程切换的开销代码都在一个线程里不需要加锁调试可以用 GDB 一直单步走下去。Cons (劣势):陷入“回调地狱”Callback Hell编码极度反人类。此外最致命的是无法利用多核 CPU因为自始至终只有一个线程在跑必须额外部署多进程架构来弥补。线程 v.s. 进程 (Threads vs. Processes)线程是操作系统的轻量级调度单位。相似点:都有自己的逻辑控制流都能被内核并发调度上下文切换。不同点:线程共享所有的代码段、数据段、堆和打开的文件描述符它们只独占极小的栈。性能差距:创建一个线程的代价远小于进程通常便宜一倍以上。这也是为什么现代 Web 服务器如 Tomcat/Go大量依赖多线程/协程并发。总结并发的方式没有银弹。进程隔离好但太笨重。事件极速且轻量但吃不到多核红利代码难写。线程开销适中但“数据共享太容易了”导致极其容易写出数据竞争Data Race的 Bug极难调试Synchronization Basics (同步的基础)并发最可怕的就是多个线程同时去读写同一个共享变量。为了理解它我们需要引入“轨迹图”模型。临界区与不安全区 (Critical Sections Unsafe Regions)临界区:操作共享变量的那几行代码。不安全区:想象一个二维坐标系X 轴是线程 1 的进度Y 轴是线程 2 的进度。如果它们的临界区在时间上重叠了这块相交的区域就是“不安全区”。一旦操作系统的调度导致程序轨迹穿过了不安全区数据就一定会被覆盖/错乱。我们的目标是通过某种机制迫使程序的运行轨迹绕过这片“雷区”。如何保证安全路径我们需要实施互斥 (Mutual Exclusion)当一个线程在临界区时其他线程绝对进不来。主流解决方案Semaphores (信号量)- 由荷兰计算机科学巨擘 Edsger Dijkstra (迪杰斯特拉) 提出。Mutex (互斥锁) 和 Condition Variables (条件变量)。信号量 (Semaphores)信号量本质上就是一个具有非负值的全局整数s只能通过两个特殊的原子操作来修改它P(s) 操作:(荷兰语 Proberen, 尝试/等待)。如果s 0则s s - 1并立刻返回。如果s 0线程就会挂起休眠直到s变为非零。V(s) 操作:(荷兰语 Verhogen, 增加/释放)。将s s 1。如果有其他线程正因为P操作在苦苦等待就唤醒其中一个。核心机制P 和 V 操作必须由硬件/操作系统保证是绝对原子的不可分割否则信号量自己也会产生数据竞争C 语言中的信号量操作 (POSIX)sem_init: 初始化信号量。如果是互斥锁Mutex初始值设为1如果是资源计数设为对应的数量。sem_wait: 对应 P 操作等待/减1。sem_post: 对应 V 操作释放/加1。Summary并发程序的开发者必须清楚地知道哪些变量是被共享的。只要是共享的就必须通过信号量或互斥锁包裹起来进行保护。Synchronization Advanced (同步的进阶)经典模型Readers-Writers 问题在数据库或缓存中常常面临这样的场景许多线程只想“读”数据少数线程想要“写”数据。由于读操作不改变数据允许多个读者同时进入是安全的但写者进入时必须绝对独占。第一类读者优先:只要还有一个读者在里面后续来的写者全部在门外排队。极端情况如果读者源源不断地到来写者将被活活饿死Starvation。第二类写者优先:只要有一个写者在排队后续新来的读者不准进入等里面的读者出来后写者立刻进去修改。极端情况如果写者太多读者会饿死。工程上往往需要更加复杂的读写锁如pthread_rwlock_t来平衡这两者。线程安全 (Thread Safety) 的四个深渊在多线程环境中调用的函数必须是线程安全的有四大类函数是绝对危险的不保护共享变量的函数。(如多个线程直接count)。在多次调用间保存状态的函数。(如依赖全局状态的伪随机数发生器rand()下一次结果依赖上一次)。返回指向静态变量指针的函数。(如经典的ctime极其危险)。内部调用了其他线程不安全函数的函数。(毒树之果)。第三类危险返回指向静态变量的指针以 C 语言标准库的ctime()为例。它会把时间计算好存放在函数内部的一个static字符数组中然后返回这个数组的指针。如果线程 A 调了ctime拿到了指针还没来得及打印线程 B 瞬间又调了一次ctime。因为它们共享那个static内存线程 A 打印出的将是线程 B 的时间解决方案Lock-and-copy (加锁复制):我们用一个包装函数Wrapper在调用原版ctime前加锁拿到结果后迅速strcpy拷贝到线程自己私有的malloc内存中再释放锁。重写为可重入函数 (Reentrant):现代的做法比如ctime_r。强迫调用者自己分配好一块内存地址传进去函数只负责把结果往那个传进来的私有地址里写彻底杜绝全局状态。Thread Level Parallelism (线程级并行)当我们迈过了同步与线程安全的这道天堑前方的道路豁然开朗。线程级并行TLP不仅是为了解决“多客户端响应”的问题更是压榨现代多核 CPU 算力的终极钥匙。把一个计算密集型大任务如图像渲染、矩阵乘法切分成小块派发给多个线程同时跑在不同的物理核心上这才是计算机系统性能起飞的最后一块拼图。全系列完结撒花 从汇编指令到内存堆栈从系统调用到网络 Socket再到最终的多线程并发。这不仅仅是 CSAPP 的落幕更是你作为一名顶级系统程序员觉醒的开端。Keep coding!

相关新闻

【Rust自学】4.1. 所有权:栈内存 vs. 堆内存

【Rust自学】4.1. 所有权:栈内存 vs. 堆内存

4.1 所有权:栈内存 vs. 堆内存 4.1.0 写在正文之前 在学习了 Rust 的通用编程概念后,就来到了整个 Rust 的重中之重——所有权。它跟其他语言都不太一样,很多初学者觉得学起来很难。这个章节就旨在让初学者能够完全掌握这个特性。 本章有五…

2026/7/23 18:47:02阅读更多 →
mpweixin 微信打卡小程序

mpweixin 微信打卡小程序

一、关键词微信打卡、积分兑换、打卡排行、惩罚措施、社交论坛二、作品包含源码数据库全套环境和工具资源本地部署教程三、项目技术前端技术: Html、Css、Js、Vue2.6、Element-ui、uniapp后端技术:Java、SpringBoot2.2.2、MyBatis-Plus四、运行环境&…

2026/7/23 18:47:02阅读更多 →
【Rust自学】4.2. 所有权规则、内存与分配

【Rust自学】4.2. 所有权规则、内存与分配

4.2 所有权规则、内存与分配 4.2.0 写在正文之前 在学习了 Rust 的通用编程概念后,就来到了整个 Rust 的重中之重——所有权。它跟其他语言都不太一样,很多初学者觉得学起来很难。这个章节就旨在让初学者能够完全掌握这个特性。 本章有五小节&#xf…

2026/7/23 18:47:02阅读更多 →
13 - VRAM驱逐与恢复

13 - VRAM驱逐与恢复

难度: 🔴 高级 预计学习时间: 2.5小时 前置知识: 第7章(内存迁移机制)、第10章(MMU Notifier集成)、dma-fence基础 📋 概述 VRAM驱逐(Eviction)是SVM中最复杂的内存管理场景之一。当GPU显存不足,或CPU需要访问已迁移到VRAM的页面时,必须将VRAM中的数据迁移回系统内…

2026/7/23 20:03:28阅读更多 →
N1盒子刷OpenWRT避坑指南:从降级到公网访问的全流程实录

N1盒子刷OpenWRT避坑指南:从降级到公网访问的全流程实录

N1盒子OpenWRT实战:从零避坑到远程访问的完整手册 手里这台N1盒子,大概是这几年折腾党们绕不开的“神机”了。百元出头的价格,配上S905D的芯片、2G内存和千兆网口,让它从一台普通的电视盒子,变成了能刷OpenWRT、Armbian、CoreELEC的“瑞士军刀”。尤其是刷成OpenWRT软路由…

2026/7/23 20:03:28阅读更多 →
OpenWrt/LEDE软路由AP模式配置实战:无缝融入现有网络

OpenWrt/LEDE软路由AP模式配置实战:无缝融入现有网络

1. 为什么要把软路由变成AP?聊聊我的真实需求 大家好,我是老张,一个在智能硬件和网络这块折腾了十多年的老玩家。今天想和大家聊聊一个非常具体,但又特别实用的场景:把一台刷了OpenWrt或LEDE的软路由,从“主路由”模式,切换成“AP模式”。 听起来有点技术?别怕,我保…

2026/7/23 20:03:28阅读更多 →
优酷路由宝YK-L1刷机实战:从Breed刷入到固件升级全解析

优酷路由宝YK-L1刷机实战:从Breed刷入到固件升级全解析

1. 为什么我们要折腾优酷路由宝YK-L1? 如果你手头有一台吃灰多年的优酷路由宝YK-L1,或者几十块钱从二手市场淘来了这么个“电子古董”,那你可算是捡到宝了。这玩意儿当年主打“能赚钱”,靠上传带宽换积分,但现在这个功能基本废了。不过,它硬件底子其实相当不错:MT7620A…

2026/7/23 20:03:28阅读更多 →
学生工作管理系统用户手册:操作指南与功能详解

学生工作管理系统用户手册:操作指南与功能详解

✅作者简介:合肥自友科技 📌核心产品:智慧校园平台(包括教工管理、学工管理、教务管理、考务管理、后勤管理、德育管理、资产管理、公寓管理、实习管理、就业管理、离校管理、科研平台、档案管理、学生平台等26个子平台) 。公司所有人员均有多…

2026/7/23 20:03:28阅读更多 →
Python在自然科学中的AI应用:高维数据处理与可解释性分析

Python在自然科学中的AI应用:高维数据处理与可解释性分析

1. 项目概述:当Python遇上自然科学中的AI在实验室泡了十年,我深刻体会到自然科学研究者面对高维数据时的痛苦——那些基因序列、气象观测、天体物理信号,动辄成千上万个维度,Excel连打开都费劲。直到五年前我开始系统地将机器学习…

2026/7/23 20:01:27阅读更多 →
Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/23 0:56:31阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/23 0:56:31阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/23 0:56:31阅读更多 →
Chitchatter完整指南:免费开源的终极点对点安全聊天工具

Chitchatter完整指南:免费开源的终极点对点安全聊天工具

Chitchatter完整指南:免费开源的终极点对点安全聊天工具 【免费下载链接】chitchatter Secure peer-to-peer chat that is serverless, decentralized, and ephemeral 项目地址: https://gitcode.com/gh_mirrors/ch/chitchatter Chitchatter是一款革命性的安…

2026/7/23 0:00:28阅读更多 →
从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表)

从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表)

更多请点击: https://intelliparadigm.com 第一章:从单点好评到指数级传播:AI副业主理人必须掌握的4层口碑渗透模型(含ROI测算表) 当AI副业主理人不再仅满足于单次服务交付,而是主动构建可复用、可裂变、可…

2026/7/23 0:00:28阅读更多 →
油泥处理设备哪里能买到

油泥处理设备哪里能买到

油泥处理设备哪里有?这是许多从事油田、炼化、清罐业务的从业者最关心的问题。根据河南三丰环保设备有限公司的行业经验,选购油泥处理设备的核心在于设备能否适配当地环保法规与原料特性,而非单纯看价格。该公司总经理王钦田先生指出&#xf…

2026/7/23 0:00:28阅读更多 →
YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

YOLOv8推理性能优化:从1.2FPS到35FPS的全链路加速实践

如果你在部署 YOLOv8 时,发现推理速度只有可怜的 1-2 FPS,而别人的演示视频却能跑到 30 FPS 以上,那么问题很可能不在模型本身,而在于你的整个处理链路。很多开发者拿到一个训练好的 YOLOv8 模型后,会直接使用官方示例…

2026/7/22 22:56:18阅读更多 →
Coze与Dify对比指南:低代码AI应用开发从入门到实战

Coze与Dify对比指南:低代码AI应用开发从入门到实战

1. 从零到一:为什么你需要了解 Coze 和 Dify?如果你对 AI 应用开发感兴趣,但一看到“大模型”、“智能体”、“工作流”这些词就头疼,觉得门槛太高,那这篇文章就是为你准备的。很多开发者,包括我自己&#…

2026/7/23 18:58:18阅读更多 →
AI生图工具怎么选?2026年6月版实测对比

AI生图工具怎么选?2026年6月版实测对比

做自媒体的朋友应该都有体会:配图一直是个让人头疼的问题。2026年,AI生图工具已经非常成熟了,但工具太多反而不知道怎么选。以下是截至2026年6月我对主流AI生图工具的实测对比。Midjourney V8.1:速度之王2026年6月11日&#xff0c…

2026/7/23 18:58:18阅读更多 →