并发编程核心原理

并发编程核心原理:可见性、一致性、原子性

一、从一个问题开始

1.1 一个"不可能"的 Bug

cpp 复制代码
#include <thread>
#include <iostream>

bool ready = false;
int data = 0;

// 线程 1:生产者
void producer() {
    data = 42;        // 步骤 1: 准备数据
    ready = true;     // 步骤 2: 标记完成
}

// 线程 2:消费者
void consumer() {
    while (!ready);   // 等待 ready 变为 true
    std::cout << data << std::endl;  // 步骤 3: 使用数据
}

int main() {
    std::thread t1(producer);
    std::thread t2(consumer);
    t1.join();
    t2.join();
    return 0;
}

问题:为什么有时会输出 0 而不是 42?

arduino 复制代码
你以为的执行顺序:
线程 1: data = 42 → ready = true
线程 2: while(!ready) → 读到 true → 读 data = 42 ✓

实际的执行顺序(可能):
线程 1: ready = true → data = 42  ← 被重排序了!
线程 2: while(!ready) → 读到 true → 读 data = 0  ✗

这就是可见性和一致性问题!


二、三大核心概念

2.1 原子性 (Atomicity)

复制代码
什么是原子性?

原子性 = 操作不可分割

就像原子一样,不能再分

示例:
┌────────────────────────────────────────┐
│  原子操作:                            │
│  ┌─────────────────┐                  │
│  │  counter++      │  一气呵成         │
│  └─────────────────┘                  │
└────────────────────────────────────────┘

非原子操作(实际是三步):
┌────────────────────────────────────────┐
│  counter++ 实际是:                    │
│  1. 读取 counter 的值                    │
│  2. 加 1                                 │
│  3. 写回 counter                         │
│                                        │
│  这三步可以被其他线程打断!             │
└────────────────────────────────────────┘

原子性问题演示:

cpp 复制代码
#include <atomic>
#include <thread>
#include <vector>
#include <iostream>

int normal_count = 0;              // 非原子
std::atomic<int> atomic_count(0);  // 原子

void test_normal() {
    for (int i = 0; i < 10000; i++) {
        normal_count++;  // 非原子:读 - 改 - 写
    }
}

void test_atomic() {
    for (int i = 0; i < 10000; i++) {
        atomic_count++;  // 原子:不可分割
    }
}

int main() {
    std::vector<std::thread> threads;
    
    // 10 个线程同时增加计数器
    for (int i = 0; i < 10; i++) {
        threads.emplace_back(test_normal);
    }
    for (auto& t : threads) t.join();
    
    std::cout << "非原子结果:" << normal_count << std::endl;
    // 期望 100000,实际可能是 50000-90000 之间的任意值
    
    threads.clear();
    normal_count = 0;
    
    for (int i = 0; i < 10; i++) {
        threads.emplace_back(test_atomic);
    }
    for (auto& t : threads) t.join();
    
    std::cout << "原子结果:" << atomic_count << std::endl;
    // 一定是 100000!
    
    return 0;
}

2.2 可见性 (Visibility)

css 复制代码
什么是可见性?

可见性 = 一个线程修改了共享变量,其他线程能立即看到这个修改

问题根源:每个线程有自己的"工作内存"

┌─────────────────────────────────────────────────┐
│              主内存 (Main Memory)                │
│              ┌───────────────┐                  │
│              │  shared_var   │                  │
│              └───────────────┘                  │
│                    ↑           ↑                │
│              刷新   │           │   刷新          │
│                    │           │                │
│  ┌─────────────────┴─┐   ┌─────┴─────────────┐ │
│  │   线程 A 工作内存   │   │   线程 B 工作内存   │ │
│  │  ┌─────────────┐  │   │  ┌─────────────┐  │ │
│  │  │shared_var=0 │  │   │  │shared_var=0 │  │ │
│  │  └─────────────┘  │   │  └─────────────┘  │ │
│  └───────────────────┘   └───────────────────┘ │
└─────────────────────────────────────────────────┘

线程 A 修改后:
┌─────────────────────────────────────────────────┐
│              主内存 (Main Memory)                │
│              ┌───────────────┐                  │
│              │  shared_var   │  ← 还是旧值!     │
│              └───────────────┘                  │
│                    ↑           ↑                │
│              还没刷新 │           │   还没刷新    │
│                    │           │                │
│  ┌─────────────────┴─┐   ┌─────┴─────────────┐ │
│  │   线程 A 工作内存   │   │   线程 B 工作内存   │ │
│  │  ┌─────────────┐  │   │  ┌─────────────┐  │ │
│  │  │shared_var=1 │  │   │  │shared_var=0 │  │ │
│  │  └─────────────┘  │   │  └─────────────┘  │ │
│  │       ↑           │   │                   │ │
│  │   A 看到了新值     │   │   B 还看到旧值!   │ │
│  └───────────────────┘   └───────────────────┘ │
└─────────────────────────────────────────────────┘

可见性问题演示:

cpp 复制代码
#include <thread>
#include <iostream>
#include <chrono>

bool flag = false;  // 普通变量,没有可见性保证

void writer() {
    std::this_thread::sleep_for(std::chrono::seconds(1));
    std::cout << "[Writer] 准备设置 flag = true" << std::endl;
    flag = true;
    std::cout << "[Writer] 已设置 flag = true" << std::endl;
}

void reader() {
    int count = 0;
    while (!flag) {
        count++;
        // 没有 flag 的更新,可能一直循环
        // 因为编译器可能优化为:
        // if (!flag) { while(true); }
    }
    std::cout << "[Reader] 检测到 flag 变化,循环次数:" << count << std::endl;
}

int main() {
    std::thread t1(writer);
    std::thread t2(reader);
    
    t1.join();
    t2.join();
    
    return 0;
}

为什么可见性会出问题?

markdown 复制代码
三个层面的可见性问题:

1. 编译器优化
   └─> 缓存变量到寄存器
   └─> 认为变量不会被其他线程修改

2. CPU 缓存
   └─> 每个 CPU 核心有自己的 L1/L2 缓存
   └─> 修改可能只在本地缓存,没写回主内存

3. 指令重排序
   └─> CPU 可能重新排列指令顺序
   └─> 为了性能优化

2.3 有序性 (Ordering)

ini 复制代码
什么有序性?

有序性 = 程序执行顺序按照代码的先后顺序

问题:指令重排序

代码顺序:           实际执行顺序:
┌──────────────┐    ┌──────────────┐
│ 1. data = 42 │    │ 2. ready = true │  ← 先执行
│ 2. ready = true│   │ 1. data = 42  │  ← 后执行
└──────────────┘    └──────────────┘

为什么可以重排序?
- 单线程下,结果一样
- CPU 为了性能,会乱序执行
- 编译器为了优化,会调整指令

多线程下,重排序导致问题!

有序性问题演示:

cpp 复制代码
#include <atomic>
#include <thread>
#include <iostream>

int data = 0;
bool ready = false;
int read_result = -1;

void writer() {
    data = 42;        // 1. 准备数据
    ready = true;     // 2. 标记完成
}

void reader() {
    while (!ready);   // 等待 ready
    read_result = data;  // 读数据
}

int main() {
    for (int i = 0; i < 1000; i++) {
        data = 0;
        ready = false;
        read_result = -1;
        
        std::thread t1(writer);
        std::thread t2(reader);
        
        t1.join();
        t2.join();
        
        if (read_result == 0) {
            std::cout << "第 " << i << " 次:读到旧值 0 (重排序导致)" << std::endl;
        }
    }
    
    return 0;
}

三、内存模型:解决三大问题

3.1 什么是内存模型?

diff 复制代码
内存模型 = 规定多线程程序中,内存访问的顺序和可见性规则

就像交通规则:
- 红灯停,绿灯行(可见性规则)
- 靠右行驶(有序性规则)
- 不能同时占用同一车道(原子性规则)

C++11 引入了正式的内存模型!

3.2 六种内存序

cpp 复制代码
enum memory_order {
    memory_order_relaxed,      // 宽松序
    memory_order_consume,      // 消费序(很少用)
    memory_order_acquire,      // 获取序
    memory_order_release,      // 释放序
    memory_order_acq_rel,      // 获取 - 释放序
    memory_order_seq_cst       // 顺序一致序(默认)
};

3.3 内存序详解

(1) memory_order_relaxed(宽松序)
diff 复制代码
特点:
- 只保证原子性
- 不保证可见性
- 不保证有序性
- 性能最高

适用场景:计数器

┌────────────────────────────────────────┐
│  线程 A: counter++ (relaxed)           │
│  线程 B: counter++ (relaxed)           │
│  线程 C: counter++ (relaxed)           │
│                                        │
│  最终结果正确(原子性保证)             │
│  但各线程看到的中间值可能不一致         │
└────────────────────────────────────────┘
cpp 复制代码
#include <atomic>
#include <thread>
#include <vector>
#include <iostream>

std::atomic<int> counter(0);

void increment() {
    for (int i = 0; i < 1000; i++) {
        // 宽松序:只要求原子性
        counter.fetch_add(1, std::memory_order_relaxed);
    }
}

int main() {
    std::vector<std::thread> threads;
    for (int i = 0; i < 10; i++) {
        threads.emplace_back(increment);
    }
    for (auto& t : threads) t.join();
    
    std::cout << "最终计数:" << counter << std::endl;
    // 一定是 10000(原子性保证)
    
    return 0;
}
(2) memory_order_acquire(获取序)
arduino 复制代码
特点:
- 用于读操作
- 保证:之后的读写不会被重排到 acquire 之前
- 保证:能看到 release 操作之前的所有写入

类比:获取一把锁
- 获取锁之后,能看到锁保护的所有数据

┌────────────────────────────────────────┐
│  线程 A:                               │
│    data = 42;                          │
│    ready.store(true, release);         │
│                                        │
│  线程 B:                               │
│    while (!ready.load(acquire));       │
│    // 这里一定能看到 data = 42         │
└────────────────────────────────────────┘
(3) memory_order_release(释放序)
diff 复制代码
特点:
- 用于写操作
- 保证:之前的读写不会被重排到 release 之后
- 保证:对 acquire 读可见

类比:释放一把锁
- 释放锁之前,所有修改都对获取锁的线程可见

┌────────────────────────────────────────┐
│  Release 操作就像一个"栅栏"             │
│                                        │
│  之前的操作  ←───┐                     │
│                  │ 不能越过栅栏         │
│  之后的操作  ←───┤                     │
│                  │                     │
│  ═══════════════════════════════════   │
│         Release 栅栏                   │
└────────────────────────────────────────┘
(4) memory_order_acq_rel(获取 - 释放序)
diff 复制代码
特点:
- acquire + release
- 用于读 - 写操作(如 exchange、fetch_add)

┌────────────────────────────────────────┐
│         获取序 │ 释放序                 │
│              ↓ ↓                       │
│  ═══════════════════════════════════   │
│         Acq_Rel 栅栏                   │
└────────────────────────────────────────┘
(5) memory_order_seq_cst(顺序一致序)
diff 复制代码
特点:
- 最严格的内存序
- 所有线程看到相同的操作顺序
- 默认选项
- 性能最低

┌────────────────────────────────────────┐
│  所有线程看到的全局顺序:               │
│                                        │
│  线程 A: 操作 1 → 操作 2 → 操作 3       │
│  线程 B: 操作 1 → 操作 2 → 操作 3       │
│  线程 C: 操作 1 → 操作 2 → 操作 3       │
│                                        │
│  就像有一个全局时钟,所有操作按顺序执行 │
└────────────────────────────────────────┘

3.4 内存序对比表

内存序 原子性 可见性 有序性 性能 适用场景
relaxed 最高 计数器
consume 部分 部分 依赖数据(很少用)
acquire 部分 中高 获取锁/标志
release 部分 中高 释放锁/标志
acq_rel 原子交换
seq_cst 默认,需要强一致

四、实际应用:从理论到代码

4.1 场景 1:自旋锁实现

cpp 复制代码
#include <atomic>
#include <thread>
#include <iostream>

class SpinLock {
    std::atomic<bool> locked{false};
    
public:
    void lock() {
        // 自旋等待,直到获取锁
        while (locked.exchange(true, std::memory_order_acquire)) {
            // 忙等待
            std::this_thread::yield();  // 让出 CPU
        }
    }
    
    void unlock() {
        // 释放锁
        locked.store(false, std::memory_order_release);
    }
};

// 为什么用 acquire-release?
/*
lock():
- exchange(true, acquire): 获取锁,看到之前持有锁的线程的所有修改
- 保证进入临界区后,能看到共享数据的最新值

unlock():
- store(false, release): 释放锁,之前的修改对下一个获取锁的线程可见
- 保证临界区内的修改对其他线程可见
*/

int shared_data = 0;
SpinLock spinlock;

void worker(int id) {
    for (int i = 0; i < 1000; i++) {
        spinlock.lock();
        // 临界区
        int temp = shared_data;
        std::this_thread::yield();  // 模拟工作
        shared_data = temp + 1;
        spinlock.unlock();
    }
}

int main() {
    std::thread t1(worker, 1);
    std::thread t2(worker, 2);
    
    t1.join();
    t2.join();
    
    std::cout << "最终值:" << shared_data << std::endl;
    // 一定是 2000
    
    return 0;
}

4.2 场景 2:无锁队列(简化版)

cpp 复制代码
#include <atomic>
#include <memory>

template<typename T>
class LockFreeQueue {
    struct Node {
        T data;
        std::atomic<Node*> next{nullptr};
        
        Node() = default;
        Node(T val) : data(val) {}
    };
    
    std::atomic<Node*> head;
    std::atomic<Node*> tail;
    
public:
    LockFreeQueue() {
        Node* dummy = new Node();
        head.store(dummy, std::memory_order_relaxed);
        tail.store(dummy, std::memory_order_relaxed);
    }
    
    ~LockFreeQueue() {
        while (Node* node = head.load(std::memory_order_relaxed)) {
            head.store(node->next.load(std::memory_order_relaxed), 
                      std::memory_order_relaxed);
            delete node;
        }
    }
    
    void enqueue(T value) {
        Node* newNode = new Node(value);
        
        while (true) {
            Node* last = tail.load(std::memory_order_acquire);
            Node* next = last->next.load(std::memory_order_acquire);
            
            // 检查 tail 是否还是最新的
            if (last == tail.load(std::memory_order_acquire)) {
                if (next == nullptr) {
                    // 尝试将新节点链接到末尾
                    if (last->next.compare_exchange_weak(next, newNode,
                            std::memory_order_release,
                            std::memory_order_relaxed)) {
                        // 链接成功,更新 tail
                        tail.compare_exchange_strong(last, newNode,
                            std::memory_order_release,
                            std::memory_order_relaxed);
                        return;
                    }
                } else {
                    // tail 落后了,帮助推进
                    tail.compare_exchange_weak(last, next,
                        std::memory_order_release,
                        std::memory_order_relaxed);
                }
            }
        }
    }
    
    bool dequeue(T& result) {
        while (true) {
            Node* first = head.load(std::memory_order_acquire);
            Node* last = tail.load(std::memory_order_acquire);
            Node* next = first->next.load(std::memory_order_acquire);
            
            if (first == head.load(std::memory_order_acquire)) {
                if (first == last) {
                    if (next == nullptr) {
                        return false;  // 队列空
                    }
                    // tail 落后了,帮助推进
                    tail.compare_exchange_weak(last, next,
                        std::memory_order_release,
                        std::memory_order_relaxed);
                } else {
                    // 读取值
                    result = next->data;
                    if (head.compare_exchange_weak(first, next,
                            std::memory_order_release,
                            std::memory_order_relaxed)) {
                        delete first;
                        return true;
                    }
                }
            }
        }
    }
};

4.3 场景 3:单例模式(双重检查锁定)

cpp 复制代码
#include <atomic>
#include <mutex>

class Singleton {
private:
    static std::atomic<Singleton*> instance;
    static std::mutex mtx;
    
    Singleton() {}
    
public:
    static Singleton* getInstance() {
        // 第一次检查(不需要锁)
        Singleton* tmp = instance.load(std::memory_order_acquire);
        if (tmp != nullptr) {
            return tmp;
        }
        
        // 需要创建实例,加锁
        std::lock_guard<std::mutex> lock(mtx);
        
        // 第二次检查(在锁内)
        tmp = instance.load(std::memory_order_relaxed);
        if (tmp == nullptr) {
            tmp = new Singleton();
            instance.store(tmp, std::memory_order_release);
        }
        
        return tmp;
    }
};

std::atomic<Singleton*> Singleton::instance{nullptr};
std::mutex Singleton::mtx;

/*
为什么这样写?

1. 第一次检查用 acquire:
   - 确保看到 instance 不为 null 时
   - 也能看到 Singleton 构造函数的所有修改

2. store 用 release:
   - 确保构造函数完成后再设置 instance
   - 其他线程看到 instance 不为 null 时,对象已完全构造

3. 不用 seq_cst:
   - acquire-release 足够保证正确性
   - 性能更好
*/

4.4 场景 4:读写锁

cpp 复制代码
#include <atomic>
#include <thread>
#include <shared_mutex>

class ReadWriteLock {
    std::atomic<int> read_count{0};
    std::atomic<bool> writing{false};
    
public:
    void lock_read() {
        // 增加读计数
        int expected = read_count.load(std::memory_order_relaxed);
        while (!read_count.compare_exchange_weak(expected, expected + 1,
                std::memory_order_acquire,
                std::memory_order_relaxed)) {
            // 重试
        }
        
        // 等待写操作完成
        while (writing.load(std::memory_order_acquire)) {
            std::this_thread::yield();
        }
    }
    
    void unlock_read() {
        read_count.fetch_sub(1, std::memory_order_release);
    }
    
    void lock_write() {
        // 等待所有读完成
        bool expected = false;
        while (!writing.compare_exchange_weak(expected, true,
                std::memory_order_acquire,
                std::memory_order_relaxed)) {
            expected = false;
            std::this_thread::yield();
        }
        
        // 等待所有读者离开
        while (read_count.load(std::memory_order_acquire) > 0) {
            std::this_thread::yield();
        }
    }
    
    void unlock_write() {
        writing.store(false, std::memory_order_release);
    }
};

五、深入理解:硬件层面

5.1 CPU 缓存架构

diff 复制代码
现代 CPU 缓存层次:

┌─────────────────────────────────────────────────┐
│              CPU Core 0    CPU Core 1           │
│              ┌─────────┐  ┌─────────┐          │
│              │  L1 缓存 │  │  L1 缓存 │          │
│              │  32KB   │  │  32KB   │          │
│              └────┬────┘  └────┬────┘          │
│                   │            │                │
│              ┌────┴────────────┴────┐          │
│              │      L2 缓存          │          │
│              │      256KB           │          │
│              └──────────┬───────────┘          │
│                         │                      │
│              ┌──────────┴───────────┐          │
│              │      L3 缓存          │          │
│              │      共享缓存         │          │
│              └──────────┬───────────┘          │
│                         │                      │
│              ┌──────────┴───────────┐          │
│              │      主内存           │          │
│              │      (DRAM)          │          │
│              └──────────────────────┘          │
└─────────────────────────────────────────────────┘

可见性问题根源:
- 每个核心有自己的 L1 缓存
- 修改可能只在本地 L1,其他核心看不到
- 需要缓存一致性协议(MESI)来同步

5.2 MESI 协议

ini 复制代码
MESI = Modified + Exclusive + Shared + Invalid

四种缓存行状态:

┌─────────────────────────────────────────────────┐
│  M (Modified): 修改态                           │
│  - 缓存行被修改,与内存不一致                   │
│  - 只有这一个缓存有该数据                       │
│  - 必须写回内存                                 │
├─────────────────────────────────────────────────┤
│  E (Exclusive): 独占态                          │
│  - 缓存行与内存一致                             │
│  - 只有这一个缓存有该数据                       │
│  - 可以直接修改(变成 M)                       │
├─────────────────────────────────────────────────┤
│  S (Shared): 共享态                             │
│  - 缓存行与内存一致                             │
│  - 多个缓存可能有该数据                         │
│  - 修改前需要通知其他缓存失效                   │
├─────────────────────────────────────────────────┤
│  I (Invalid): 无效态                            │
│  - 缓存行无效                                   │
│  - 需要重新从内存或其他缓存加载                 │
└─────────────────────────────────────────────────┘

状态转换:
        读                 写
I ─────────> E ─────────> M
             ↑           ↓
             └──── S <───┘

5.3 内存屏障 (Memory Barrier)

css 复制代码
什么是内存屏障?

内存屏障 = 指令,告诉 CPU 不要重排屏障两侧的指令

就像一堵墙:

┌────────────────────────────────────────┐
│  指令 A                                │
│  指令 B                                │
│          ↓                             │
│  ════════════════════                  │
│       内存屏障                         │
│  ════════════════════                  │
│          ↑                             │
│  指令 C                                │
│  指令 D                                │
│                                        │
│ A 和 B 可以在屏障前重排                 │
│ C 和 D 可以在屏障后重排                 │
│ 但 A/B 不能和 C/D 跨越屏障重排          │
└────────────────────────────────────────┘

C++ 中的内存屏障:

std::atomic_thread_fence(std::memory_order_acquire);
std::atomic_thread_fence(std::memory_order_release);
std::atomic_thread_fence(std::memory_order_seq_cst);

5.4 虚假共享 (False Sharing)

c 复制代码
什么是虚假共享?

多个线程修改同一缓存行的不同变量,导致缓存行频繁失效

┌─────────────────────────────────────────────────┐
│           缓存行 (64 字节)                       │
│  ┌──────┬──────┬──────┬──────┬──────┬──────┐  │
│  │ varA │ varB │ varC │ varD │ ...  │ ...  │  │
│  └──────┴──────┴──────┴──────┴──────┴──────┘  │
│     ↑                      ↑                  │
│  线程 1 修改              线程 2 修改            │
│                                        │
│  虽然修改的是不同变量,但在同一缓存行!  │
│  导致缓存行在两个核心间来回同步          │
│  性能严重下降!                         │
└─────────────────────────────────────────────────┘

解决方案:内存对齐

struct alignas(64) PaddedCounter {
    std::atomic<int> counter1;
    char padding1[60];  // 填充到 64 字节
    
    std::atomic<int> counter2;
    char padding2[60];  // 填充到 64 字节
};

六、最佳实践总结

6.1 选择内存序的决策树

arduino 复制代码
                    ┌─────────────────┐
                    │  需要原子操作吗? │
                    └────────┬────────┘
                             │
              ┌──────────────┼──────────────┐
              │ 否           │ 是           │
              │              ▼              │
              │       ┌─────────────┐       │
              │       │ 需要可见性吗?│       │
              │       └──────┬──────┘       │
              │              │              │
              │    ┌─────────┼─────────┐    │
              │    │ 否      │ 是      │    │
              │    ▼         ▼         │    │
              │  relaxed  需要强顺序?   │    │
              │              │         │    │
              │    ┌─────────┼─────────┐│    │
              │    │ 否      │ 是      ││    │
              │    ▼         ▼         ││    │
              │ acquire-  seq_cst     ││    │
              │ release               ││    │
              └───────────────────────┘│    │
                                       ▼    ▼
                                  用 mutex 或 seq_cst

6.2 实用建议

cpp 复制代码
// 1. 默认使用 seq_cst(最简单,不容易错)
std::atomic<int> counter(0);  // 默认 seq_cst
counter++;  // 安全

// 2. 计数器用 relaxed(性能最好)
std::atomic<int> stats(0);
stats.fetch_add(1, std::memory_order_relaxed);

// 3. 标志位用 acquire-release
std::atomic<bool> ready(false);

// 写
ready.store(true, std::memory_order_release);

// 读
if (ready.load(std::memory_order_acquire)) {
    // 能看到写之前的所有数据
}

// 4. 复杂场景用 mutex
std::mutex mtx;
std::lock_guard<std::mutex> lock(mtx);
// 简单、安全、不容易错

// 5. 避免在原子变量上依赖顺序
std::atomic<int> x(0), y(0);

// 不推荐:依赖 x 和 y 的顺序
x.store(1, std::memory_order_release);
y.store(1, std::memory_order_release);

// 推荐:用 seq_cst 或 mutex

6.3 调试技巧

cpp 复制代码
// 1. 使用 ThreadSanitizer 检测数据竞争
// 编译时加 -fsanitize=thread
g++ -fsanitize=thread -g program.cpp -o program

// 2. 使用原子变量的 debug 模式
#ifdef DEBUG
    #define ATOMIC_OP(op, var, ...) do { \
        std::cout << "Thread " << std::this_thread::get_id() \
                  << ": " << #var << "." << #op << std::endl; \
        var.op(__VA_ARGS__); \
    } while(0)
#else
    #define ATOMIC_OP(op, var, ...) var.op(__VA_ARGS__)
#endif

// 3. 添加日志追踪
class TrackedAtomic {
    std::atomic<int> value;
    std::string name;
public:
    TrackedAtomic(const std::string& n) : name(n) {}
    
    void store(int v) {
        std::cout << "[" << name << "] store " << v 
                  << " (thread " << std::this_thread::get_id() << ")" << std::endl;
        value.store(v);
    }
    
    int load() {
        int v = value.load();
        std::cout << "[" << name << "] load " << v
                  << " (thread " << std::this_thread::get_id() << ")" << std::endl;
        return v;
    }
};

七、总结

7.1 核心概念回顾

概念 问题 解决方案
原子性 操作被中断 atomic 操作
可见性 修改看不到 acquire-release / 锁
有序性 指令重排序 内存屏障 / seq_cst

7.2 一句话总结

复制代码
原子性:操作要么全做,要么全不做
可见性:我的修改你能看到
有序性:代码顺序就是执行顺序

内存模型:规定这三者的规则

7.3 实际开发建议

arduino 复制代码
1. 优先使用高级抽象
   mutex > 原子操作 > 手动内存序

2. 默认用 seq_cst
   除非性能瓶颈,再考虑 relaxed/acquire-release

3. 减少共享
   线程本地存储 > 共享变量

4. 使用成熟库
   不要自己实现无锁数据结构

5. 用工具检测
   ThreadSanitizer、helgrind 等
相关推荐
用户3210442819452 小时前
STL详解
面试
IT当时语_青山师__JAVA技术栈3 小时前
Java反射深度解析:运行时探查的艺术、代价与工程实践
java·后端·面试
卡次卡次13 小时前
14.1: 总结本章 Python 高性能并发:多线程+多进程核心知识点+实战指南(面试/开发双适配)
服务器·python·面试
辛苦才能3 小时前
数据结构-排序算法-堆排序(重点比赛面试经常考)
数据结构·面试·排序算法
ximu_polaris4 小时前
C++高频面试题汇总
c++·面试
中小企业实战军师刘孙亮4 小时前
中小实体如何逆势稳健发展?重塑经营逻辑是关键!佛山鼎策创局破局增长咨询
学习·面试·创业创新·制造·学习方法
人道领域4 小时前
【LeetCode刷题日记】二叉树层序遍历完全指南:从基础到LeetCode实战一篇搞定BFS模板,秒杀4道经典面试题
java·开发语言·数据结构·leetcode·面试·二叉树
QD_ANJING4 小时前
建议5月的Web前端开发都去飞书上准备面试...
前端·人工智能·面试·职场和发展·前端框架·状态模式·ai编程
研究点啥好呢4 小时前
面馆开业!客官,你的面(经)好了!
python·阿里云·docker·面试·reactjs·求职招聘·react