惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

GbyAI
GbyAI
Cyberwarzone
Cyberwarzone
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
W
WeLiveSecurity
博客园 - 叶小钗
Hugging Face - Blog
Hugging Face - Blog
Security Latest
Security Latest
Scott Helme
Scott Helme
TaoSecurity Blog
TaoSecurity Blog
N
Netflix TechBlog - Medium
爱范儿
爱范儿
Application and Cybersecurity Blog
Application and Cybersecurity Blog
G
Google Developers Blog
F
Fortinet All Blogs
N
News and Events Feed by Topic
V2EX - 技术
V2EX - 技术
Google Online Security Blog
Google Online Security Blog
L
LINUX DO - 热门话题
NISL@THU
NISL@THU
The GitHub Blog
The GitHub Blog
Spread Privacy
Spread Privacy
S
Secure Thoughts
T
Tailwind CSS Blog
Google DeepMind News
Google DeepMind News
Recorded Future
Recorded Future
N
News and Events Feed by Topic
SecWiki News
SecWiki News
S
Security @ Cisco Blogs
A
About on SuperTechFans
云风的 BLOG
云风的 BLOG
L
Lohrmann on Cybersecurity
P
Palo Alto Networks Blog
Know Your Adversary
Know Your Adversary
IT之家
IT之家
人人都是产品经理
人人都是产品经理
Attack and Defense Labs
Attack and Defense Labs
Hacker News - Newest:
Hacker News - Newest: "LLM"
MyScale Blog
MyScale Blog
宝玉的分享
宝玉的分享
T
The Blog of Author Tim Ferriss
H
Hacker News: Front Page
T
Tenable Blog
C
CERT Recently Published Vulnerability Notes
D
DataBreaches.Net
阮一峰的网络日志
阮一峰的网络日志
Help Net Security
Help Net Security
博客园_首页
S
Securelist
罗磊的独立博客

博客园_首页

Linux实操--组管理、权限管理和定时任务 Java + EasyExcel 实现单个接口导出多个Excel Mem0 源码解析系列(二):提示词工程的深度剖析 Openclaw TaskFlow究竟是什么?和普通Skill技能有什么区别 博文阅读密码验证 - 博客园 嘉立创开源:应该是全网MicroPython教程最多的开发板 Hermes Agent 集成实践:从协议到生产 2026年AI编程工具横评:Cursor、Codex、Claude Code、Zed、Windsurf Java程序员必看的RAG入门教程 2026 AI效率神器:Superpowers + Claude Code 保姆级教程 本地大模型部署全攻略:从 0 到 1 玩转 Ollama 【从0到1构建一个ClaudeAgent】内存管理-上下文压缩 .NET 高级开发 | 设计、实现一个事件总线框架 电子小白入门之NE555 3. WorkBuddy:隐藏玩法,一键召唤专家,让 AI 以"专家身份"给你干活 和AI一起搞事情#3:Claude Teammate 游戏开发翻车实录 【OpenClaw】通过 Nanobot 源码学习架构---(7)Memory C# .NET 周刊|2026年3月3期 我在 Debian 11 上把 K8s 单机搭起来了,过程没你想的那么顺(/opt 目录版) 深度学习进阶(七)Data-efficient Image Transformer CLI+Skill搭建浏览器AI自动化框架,告别一切重复枯燥任务 告别Token账单无底洞:OpenClaw本地部署,重塑企业数据主权的唯一解 FastAPI+Vue:文件分片上传+秒传+断点续传,这坑我帮你踩平了! SBTI 爆火后,我做了个程序员版的 CBTI。。已开源 + 附开发过程 多模态检索开始进入工程期:用 Sentence Transformers 搭建可落地的 Multimodal RAG 100多行代码实现一个最简单的Agent(用ReAct) Claude Code 通关手册(八):推荐 5 个 Hooks,代码质量提升 3 倍 老板:“有人截图了!”。安全部门:“收到,马上查暗水印!” - why技术 技术之外,皆是人间 C#/.NET/.NET Core技术前沿周刊 | 第 69 期(2026年4.01-4.12) Snack JSONPath 项目架构分析 Claude Code Buddy 小析:一个非核心功能,如何体现产品的细节完成度 AI新时代下的图床管理方案-Cloudflare图床+MCP+Skills方案指南 化繁为简:顺丰速运App如何通过 HarmonyOS SDK实现专业级空间测量 从零实现富文本编辑器#13-React非编辑节点的内容渲染 AI开发-python-langchain框架(3-23-OpenAI Functions风格Tool Calling智能助手) .NET + AI 进阶实战:基于类的技能开发 - 打造可治理的 Agent 能力模块 【从0到1构建一个ClaudeAgent】规划与协调-技能 上周热点回顾(4.6-4.12) 电子小白的工具三件套:面包板、杜邦线、万能板 单表五亿数据的查询优化 | Mysql、StarRocks 2. WorkBuddy:从“我是谁”到“帮我干活” C# 如何减少代码运行时间:7 个实战技巧 基于HelixToolkit.SharpDX 渲染3D模型 - 笺上知微 从零开始的双臂具身VLA起源及现阶段发展综述 - SkyXZ 记对 xonsh shell 的使用, 脚本编写, 迁移及调优 - pluvium27 受够了Vibe Coding的失控?换个起点,让AI事半功倍 从开始配置漏洞环境到漏洞复现流程 - 難しい 关于10年工作经验的程序员对OpenClaw的实战经验分享以及看法 - 虚无境 Any metadata 的内存布局 C# .NET 周刊|2026年3月2期 - InCerry 我帮你测过了,测试圈排名第二的 Skill 依然很牛逼 Skill Discovery | 无监督技能发现的经典工作总结 - MoonOut PbootCMS 网站内容数量多导致访问慢?这些实用优化方案帮你提速! - 家兴网络技术工作室 上下文工程是什么?过时了么?一文讲明白! - 一枫说码 网站漏洞怎么发现并修复?一篇实用指南(附完整流程) - 家兴网络技术工作室 开了 TUN 模式还是直连?90% 的人都踩过这个坑 Github日报|2026年04月12日 - AI一族 AScript扩展多种脚本语言 - rockey627 AI 学习笔记:Agent 的记忆机制 你能被装进一个文件里吗?——7 万人把同事"蒸馏"成了 AI - 我没有三颗心脏 Claude Code 通关手册(七):给 AI 装上技能包——Skills 完全指南 - 暮色之狐 在浏览器中快速编辑代码:VSCode Web 集成实践 - Newbe36524 蒸馏自己 skill?基于 Deepseek 的蒸馏器,丐版蒸馏方式,简单便捷 - To_Carpe_Diem Spring AI Aliababa和AgentScope,哪个更好? - 苏三说技术 Etsy 把 1000 个 MySQL 分片迁进 Vitess:425TB 数据背后的真正问题不是性能,而是运维规模 MicroPython LVGL基础知识和概念:底层渲染与性能优化 - FreakStudio 数据库草图算法 Python 潮流周刊#146:CPython 引入 Rust 的进展 - 豌豆花下猫 最小生成树 - mofei1116 红日靶场七:从外网入口、容器逃逸到 AD 接管的完整利用链复盘 - YouDiscovered1t 分享四款开源且实用的 Kafka 管理工具 - 追逐时光者 vLLM 权重加载机制全解析:从挑战到理想架构 LCT 学习笔记 - ACehomoxue Avalonia UI 12.0.0 正式发布:架构演进和性能飞跃 - 张善友 当 AI Agent 把调用链拉长,延迟开始成为一门生意 conhost.exe 无法显示 U+2717 - 145a 太秀了,我把自己蒸馏成了 Skill!已开源 - 程序员鱼皮 ASP.NET Core 内存缓存实战:一篇搞懂该怎么配、怎么避坑 基于 Ghostty 带有分割标签页和为 Claude 编程设计的通知终端 - BugShare AI 焊死入口:教育的“操作系统级”重塑 - 郝hai 初级Java开发工程师使用sql脚本编写代码的过程是简单而且不糊涂 - CoderOilStation Claude Code通关手册(六):MCP协议完全指南 - 暮色之狐 边框灯光环绕动画特效实现指南 - Newbe36524 开源:子木蒸馏版的 SEO 审计工具 seo-audit-skill v1.0 我所理解的Python元模型 【从0到1构建一个ClaudeAgent】规划与协调-TodoWrite - 程序员Seven Claude 和 Codex 在审计 Skill 上性能差异探究 - ACai_sec AScript如何实现中文脚本引擎 - rockey627 【渗透测试】HTB Season10 Garfield 全过程wp - dynasty_chenzi Android 开发者为什么必须掌握 AI 能力?端侧视角下的技术变革 树状数组正确性证明 - AC-wyr 你的 AI 焦虑,可能比 AI 本身更危险——ATM 机没有消灭银行柜员,但恐慌消灭了你的判断力 - 我没有三颗心脏 一个拉胯的分库分表方案有多绝望?整个部门都在救火! - 冰河团队 动态规划入门必学之走方格问题 - Ofnoname PostgREST 与 PostgreSQL 角色权限配置全解析(生产级实践) - SheepDog1998 使用 UEFI 图形输出协议 GOP 在屏幕上显示图像的方法 - 阿源- Claude Code通关手册(五):组建你的AI专家团队,子代理系统 - 暮色之狐 一个程序员到架构师的催婚路之感悟(整整10年后的催婚相亲感悟) - MisterLip 用 Agent Skill 自动生成工作周报 - 赵康
并发编程核心概念辨析
同勉共进 · 2026-04-19 · via 博客园_首页

本文旨在辨析并发编发中的常见核心概念,目的是防止初学者在学习过程中对相关概念一知半解,互相混淆,越学越懵。本文以澄清概念为主,对部分知识点,比如 MESI 缓存一致性协议,不会深入介绍,感兴趣的读者请自行学习。

一、背景:CPU 多级缓存架构

为了读者在阅读后序章节时,有更清晰、更形象的认知,这里放上现代 CPU 缓存的典型结构。一图胜千言,不多赘述,仅陈述以下要点:

  • 一颗 CPU,往往有多个核心(core)。同一时刻,每个 core 上都可以运行一个线程(thread)。
  • 为了追求更高的效率:
    • 编译器 / CPU 可能重排某些指令,把后面的提前执行。
    • CPU 增加了多级缓存。

这使得某些情况下,某个线程看到的数据,可能是非预期的,进而导致程序出现逻辑错误。

当然,这并不是说编译器或者 CPU 的设计有缺陷,而是一种平衡与妥协:为了效率, 编译器 / CPU 会适度“放宽政策”,不做过于严苛的约束和检查,这在多数情况下是安全的;为了正确性,在并发编程时,开发者需要更加精细的干预。

🔍 点击查看图片 CPU cache

二、概念辨析

1. 缓存一致性(Cache Coherence)

层次:纯硬件机制,在 CPU 内部固化。

解决的问题同一个内存地址(单一变量)多个 CPU 核心的本地缓存(L1/L2 Cache)中如何同步。即,一个核心的写入如何让其它核心感知?

核心0的L1缓存:  [addr X] = 1  ← 我刚写入
核心1的L1缓存:  [addr X] = 0  ← 这里还是旧值,怎么同步?

机制:MESI、MOESI、MESIF协议(状态机)。注意,这些缓存一致性协议是固化在CPU内部的,纯硬件实现。

M (Modified)  - 该 cache line 仅存在当前 cache 中,与内存不一致(dirty),其他 CPU core 缓存无效
E (Exclusive) - 该 cache line 仅存在当前 cache 中,与内存一致(clean)
S (Shared)    - 该 cache line 被多核共享,且与内存一致
I (Invalid)   - 该 cache line 已失效,需重新加载

关键特性

  • 地址/变量
  • 最终一致,但不保证何时能看到(因为有 store buffer)。
  • 不保证多个地址之间的对外可见顺序(核心1可能先看到先看到修改后的变量A,后看到修改后的变量B;核心2可能正好赶过来)。
  • 对程序员完全透明,你不需要也不能直接操控它。

通俗理解:它是底层的“群消息同步机制”,保证群里所有人看到的单条消息内容是一致的。

2. 内存一致性模型(Memory Consistency Model)

层次:硬件架构规范层,由 CPU 架构决定,不同类型的 CPU 不一样。

解决的问题:多个地址上的读写操作,从其它核心观察时,顺序会不会乱?允许哪些重排?

这才是真正决定"多线程程序正确性"游戏规则的模型:

                                                  允许的重排
模型                           Load-Load   Load-Store  Store-Store  Store-Load
───────────────────────────────────────────────────────────────────────────────────────
Sequential Consistency (SC)        ×           ×            ×           ×
Total Store Order (TSO)            ×           ×            ×           ✓  ← 只允许这个
Relaxed / Weak Consistency         ✓           ✓           ✓           ✓

不同架构的选择

  • x86/x64:TSO(Total Store Order)——接近最强,只允许 Store→Load 重排。
  • ARM:非常宽松(weakly ordered),几乎允许所有重排。
  • POWER:类似 ARM,甚至更宽松。

缓存一致性 vs 内存一致性模型的区别

  • 缓存一致性回答:一个地址的写入最终会传播吗?答:会(最终所有核心一定能看到一致结果)
  • 内存一致性模型回答:多个地址的操作,以什么顺序传播?是否允许后操作的先被看到?

通俗理解:你发了消息 A,又发了消息 B。内存一致性模型决定了,群里其他人有没有可能先看到 B,后看到 A。

3. 内存屏障(Memory Barrier / Fence)

层次:硬件指令 + 编译器指令

解决的问题:在代码的特定位置,强制约束重排序边界。是程序员/编译器用来干预“内存一致性”的物理武器。

类型

  • Load Barrier:屏障前的所有 load 操作,必须在屏障后的任何 load 操作开始之前,全局完成(对其他处理器可见)。
  • Store Barrier:屏障前的所有 store 操作,必须在屏障后的任何 store 操作开始之前,全局完成(从 Store Buffer 刷新到 L1 Cache,并对其他处理器可见)
  • Full Barrier: 屏障前的所有读和写操作,必须在屏障后的任何读和写操作开始之前,全局完成。

具体指令

x86:
  LFENCE  → Load Barrier
  SFENCE  → Store Barrier  
  MFENCE  → Full Barrier(最常用)
  LOCK前缀 → 隐含 Full Barrier

ARM:
  DMB ISH   → Full Barrier(数据内存屏障)
  DSB ISH   → 更强的同步屏障
  ISB       → 指令同步屏障(刷流水线)

两种屏障(注意区分):

// 编译器屏障(只防止编译器重排,CPU不受限)
asm volatile("" ::: "memory");          // GCC
_ReadWriteBarrier();                    // MSVC

// 硬件屏障(同时防止编译器重排 + CPU重排)
asm volatile("mfence" ::: "memory");   // x86 Full Barrier

4. 内存序(Memory Order)

层次:C++ 编程语言层,C++11 引入。

本质:是对内存屏障的高级抽象,让程序员用语义而非汇编指令来表达需求。

机制:你写下memory_order,编译器会根据当前的 CPU 架构(x86 还是 ARM),自动帮你翻译成对应架构的内存屏障指令(Memory Barrier)。

六个级别

relaxed    → 只保证原子性,不产生任何屏障
acquire    → Load 时用:屏障后的操作不能重排到此 Load 之前
release    → Store 时用:屏障前的操作不能重排到此 Store 之后
acq_rel    → 用于 RMW:同时具备 acquire + release 语义
consume    → acquire 的弱化版(实践中几乎不用)
seq_cst    → 最强:全局顺序一致,等价于 Full Barrier

编译器如何翻译

C++ memory_order          x86 生成          ARM 生成
─────────────────────────────────────────────────────
relaxed load          →   MOV              LDR
relaxed store         →   MOV              STR
acquire load          →   MOV              LDAR
release store         →   MOV              STLR
seq_cst store         →   MOV + MFENCE     STLR + DMB
seq_cst load          →   MOV              LDAR

x86 上 acquire/release 不需要额外指令(因为 TSO 已经提供了大部分保证),ARM 上需要专用指令。

5. 四者关系

🔍 点击查看图片 relationship

它们的依赖关系

  • 缓存一致性:硬件底座,没有它,写入根本无法传播,其他一切无从谈起。
  • 内存一致性模型:硬件架构契约与规则,定义了默认允许什么、禁止什么。
  • 内存屏障:硬件指令,是工具和手段:当默认规则不够用时,用它来强化约束。
  • 内存序:是软件层高级抽象,C++ 程序员通过它告诉编译器需要什么保证

三、内存序/内存屏障的作用范围

作用1:防止当前线程内的指令重排(编译器 + CPU)

// 没有屏障,编译器和CPU可能重排这两条指令
data = 42;          // 可能被移到 flag store 之后!
flag = true;

// 有 release 屏障,data = 42 一定在 flag = true 之前完成
data = 42;
flag.store(true, memory_order_release);  // 屏障

作用2:控制跨线程的可见性时序

通过控制“store 何时变得全局可见”和“load 何时看到最新值”来影响其他线程的观察结果

内存屏障的物理效果(以 x86 TSO 为例):

CPU Core 0:                    Store Buffer         Cache(共享)
─────────────────────────────────────────────────────────────
store data = 42    → 进入 Store Buffer →  [等待提交]
store flag = true  → 进入 Store Buffer →  [等待提交]
MFENCE             → 强制刷新 Store Buffer  → data=42, flag=true 提交到 Cache

CPU Core 1:                                         Cache(共享)
─────────────────────────────────────────────────────────────
load flag          ← 从 Cache 读(必须看到 flag=true 后才能继续)
MFENCE / acquire   ← 确保后续 load 看到最新 Cache 状态
load data          ← 从 Cache 读 → 一定是 42

关键点

  • 内存屏障强制刷新 Store Buffer,让 store 提交到 Cache(对其他核心可见)
  • 缓存一致性协议(MESI)负责在 Cache 之间传播这个更新
  • acquire load 确保从 Cache 读时看到最新状态(不使用过期的缓存行)

所以:

防重排(编译器/CPU内部)+ 强制可见性(跨线程)
         ↑                       ↑
         同一个机制,两种效果,不可分割

四、Store-Load 重排

1. 原因

根本原因:Store Buffer(写缓冲区)

当 CPU 执行写操作(Store)时,如果直接写入L1 Cache,由于多核之间的“缓存一致性协议(如MESI)”,CPU 必须等待其他核心确认并作废它们对应的缓存行,这个等待过程比较漫长(CPU 视角)。为了不阻塞 CPU,核心会先把数据写到 Store Buffer 中,然后继续执行后续指令。

现代 CPU 架构:

CPU Core
  ↓ store
[Store Buffer]  ← store 先写这里(速度快,不用等Cache响应)
  ↓ 异步刷新
[L1 Cache]
  ↓
[L2 Cache]
  ↓
[LLC / 内存]
问题场景(Dekker 互斥算法的经典失败案例):

初始值:X = 0, Y = 0

Thread 1:              Thread 2:
  store X = 1            store Y = 1
  load R1 = Y            load R2 = X

期望:R1=1 或 R2=1 至少有一个成立
实际:R1=0 且 R2=0 竟然可能发生!(x86上也会!)

时序解析

Time →
Thread 1: store X=1 → [Store Buffer]    ← 还没提交到 Cache!
Thread 1: load  Y=0 ← 从 Cache 读(Y 的 store 还在 Thread2 的 Store Buffer 里)
Thread 2: store Y=1 → [Store Buffer]    ← 还没提交到 Cache!
Thread 2: load  X=0 ← 从 Cache 读(X 的 store 还在 Thread1 的 Store Buffer 里)
// 结果:R1=0, R2=0。两个 store 都"消失了"

Store-Load 重排的本质:不是 CPU 真的"调换了顺序",而是 store 在 store buffer 里异步等待,而 load 已经直接去 cache 读了。效果上等价于 load 跑到了 store 之前。

Store-Load 重排的理解:从外部观察者(其它 CPU core)的视角看,load 跑到 store 的前头了。因为从外部观察者的立场来看,store完成的标志,是“你得让我看见”。现在,在没有让我看到你写的值的情况下,你先执行了后面的 load 指令,那对我来说,你就是先读后写了。

注意:x86/TSO 只允许 Store→Load 重排,其他三种(Load-Load, Load-Store, Store-Store)x86 不允许。ARM 四种都允许。

2. 解决方案

方法1:在 store 和 load 之间插入 Full Barrier

// x86
asm volatile("mfence" ::: "memory");

// C++ 标准方式
std::atomic_thread_fence(std::memory_order_seq_cst);
Thread 1:
  store X = 1
  MFENCE          ← 强制刷新 Store Buffer,X=1 提交到 Cache
  load R1 = Y     ← 此时 Y 的最新值一定可见

Thread 2:
  store Y = 1
  MFENCE
  load R2 = X     ← 此时 X=1 一定可见

方法2:使用 seq_cst 原子操作

std::atomic<int> X{0}, Y{0};

// Thread 1
X.store(1, std::memory_order_seq_cst);   // 含隐式 Full Barrier
int r1 = Y.load(std::memory_order_seq_cst);

// Thread 2  
Y.store(1, std::memory_order_seq_cst);
int r2 = X.load(std::memory_order_seq_cst);

// 保证:r1=1 或 r2=1 至少一个成立