关于 memory-model
memory-model 是一项面向 Agent 的 C++ 和 Rust 内存模型技能,涵盖内存序、happens-before、原子操作与栅栏,并提供无锁结构的实践模式。
主要功能
- 01讲解 C++ 与 Rust 内存序
- 02说明 happens-before 关系
- 03指导原子操作与栅栏
- 04提供无锁结构实践模式
- 05整理常见错误与修正
适用场景
- 01为原子操作选择内存序
- 02理解 acquire-release 与 seq_cst
- 03设计线程间数据发布
- 04实现无锁队列或自旋锁
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ mohitmishra786/ low-level-dev-skills --skill memory-model安装后在 Claude Code 中输入/memory-model确认已加载。
附带文件references/cpp-memory-ordering.md
详细介绍
内存模型
目的
指导 Agent 理解 C++ 和 Rust 内存模型:内存顺序、先行发生关系、原子操作、栅栏,以及无锁数据结构的实用模式。
触发条件
- “什么是 C++ 内存模型?”
- “原子操作应该使用哪种内存顺序?”
- “获取-释放和 seq_cst 有什么区别?”
- “如何在 C++ 中使用 std::atomic?”
- “Rust 原子操作中的获取/释放是什么?”
- “如何实现无锁队列?”
工作流
1. 内存顺序概述
现代 CPU 和编译器会为了提高性能而重新排序操作。内存模型规定了允许进行哪些重排序,以及如何实现同步。
Ordering strength (weakest to strongest):
Relaxed < Release/Acquire < AcqRel < SeqCst
Stronger ordering = more synchronization = more correct, but slower
Weaker ordering = fewer barriers = faster, but needs careful analysis
2. 内存顺序
| 顺序 | C++ | Rust | 含义 |
|---|---|---|---|
| Relaxed(宽松) | memory_order_relaxed | Ordering::Relaxed | 不保证顺序;仅保证原子性 |
| Consume(消费) | memory_order_consume | (使用 Acquire) | 数据依赖顺序 |
| Acquire(获取) | memory_order_acquire | Ordering::Acquire | 此加载会看到匹配的 release 之前的所有写入 |
| Release(释放) | memory_order_release | Ordering::Release | 此存储之前的所有写入对 acquire 可见 |
| AcqRel(获取-释放) | memory_order_acq_rel | Ordering::AcqRel | 对 RMW 操作同时执行获取和释放 |
| SeqCst(顺序一致) | memory_order_seq_cst | Ordering::SeqCst | 所有 seq_cst 操作之间存在全序 |
3. C++ std::atomic
#include <atomic>
#include <thread>
std::atomic<int> counter{0};
std::atomic<bool> ready{false};
// Producer thread
void producer() {
data = 42; // (1) write data
ready.store(true, std::memory_order_release); // (2) signal
}
// Consumer thread
void consumer() {
while (!ready.load(std::memory_order_acquire)); // (3) wait
assert(data == 42); // (4) guaranteed to see (1)
}
获取-释放保证:如果线程 A 对 X 执行释放存储,而线程 B 执行的获取加载读到了 A 写入的值,那么 A 在释放之前的所有写入,对 B 在获取之后都可见。
4. 选择合适的内存顺序
Use case?
├── Counter (just needs atomicity, order irrelevant) → Relaxed
├── Reference counting (decrement + final check) → AcqRel (dec), Acquire (load 0 check)
├── Publish data from one thread to another → Release (store), Acquire (load)
├── Mutual exclusion / mutex implementation → AcqRel / SeqCst
├── Lock-free queue multiple producers/consumers → SeqCst (safest to start)
└── Sequence number check (simple flag) → Release + Acquire
5. 常见模式
// Pattern 1: Spinlock
class Spinlock {
std::atomic_flag flag = ATOMIC_FLAG_INIT;
public:
void lock() {
while (flag.test_and_set(std::memory_order_acquire))
; // spin
}
void unlock() {
flag.clear(std::memory_order_release);
}
};
// Pattern 2: Reference counting
class RefCounted {
std::atomic<int> refcount{1};
public:
void addref() {
refcount.fetch_add(1, std::memory_order_relaxed); // only need atomicity
}
void release() {
if (refcount.fetch_sub(1, std::memory_order_acq_rel) == 1) {
// AcqRel ensures we see all writes from other releasers
delete this;
}
}
};
// Pattern 3: One-time initialisation
class LazyInit {
std::atomic<void*> ptr{nullptr};
std::mutex mtx;
public:
void* get() {
void* p = ptr.load(std::memory_order_acquire);
if (p == nullptr) {
std::lock_guard lock(mtx);
p = ptr.load(std::memory_order_relaxed);
if (p == nullptr) {
p = create();
ptr.store(p, std::memory_order_release);
}
}
return p;
}
};
6. Rust 原子操作
use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
use std::sync::Arc;
// Simple counter
let counter = Arc::new(AtomicUsize::new(0));
// Increment
counter.fetch_add(1, Ordering::Relaxed);
// Read
let val = counter.load(Ordering::Relaxed);
// Publish/subscribe pattern
static READY: AtomicBool = AtomicBool::new(false);
// Publisher thread
unsafe { DATA = 42; } // Write data
READY.store(true, Ordering::Release); // Signal
// Subscriber thread
while !READY.load(Ordering::Acquire) {}
let d = unsafe { DATA }; // Safe: guaranteed to see publisher's write
7. 栅栏
栅栏无需针对特定变量执行原子操作,即可提供顺序保证:
// C++ fence — equivalent to a global memory barrier
std::atomic_thread_fence(std::memory_order_acquire); // Acquire fence
std::atomic_thread_fence(std::memory_order_release); // Release fence
// Typical use: multiple atomic writes then one fence
relaxed_atomic_a.store(1, std::memory_order_relaxed);
relaxed_atomic_b.store(2, std::memory_order_relaxed);
std::atomic_thread_fence(std::memory_order_release); // barrier for all above
sentinel.store(true, std::memory_order_relaxed);
8. 常见错误
| 错误 | 修正方法 |
|---|---|
| 在发布/订阅中使用 Relaxed | 存储时使用 Release,加载时使用 Acquire |
| 到处使用 SeqCst | 先进行性能分析;使用满足要求的最弱顺序 |
| 忘记非原子加载不是原子的 | 所有共享可变数据都需要使用原子变量或互斥锁 |
在 C++ 中使用 volatile 保证线程安全 | volatile 不是内存顺序工具;应使用 atomic |
| 在未使用 SeqCst 的情况下假设顺序一致性 | 不同平台的默认一致性各不相同 |
有关内存顺序规则和先行发生关系的参考,请参阅 references/cpp-memory-ordering.md。
相关技能
- 使用
skills/runtimes/sanitizers——TSan 可检测涉及非原子访问的数据竞争 - 使用
skills/rust/rust-sanitizers-miri,通过 Miri 检测 Rust 内存顺序违规 - 使用
skills/low-level-programming/assembly-x86理解生成的栅栏指令 - 使用
skills/debuggers/gdb,通过检查线程调试并发程序
评论
支持 Markdown