跳转到内容

C++ 原子操作

来自代码酷


介绍[编辑 | 编辑源代码]

原子操作(Atomic Operations)是C++多线程编程中的核心概念,指在单个CPU指令内完成的操作,不会被线程调度机制打断。这类操作用于确保多线程环境下对共享数据的修改是线程安全的,无需显式使用互斥锁(Mutex)。C++11起通过std::atomic模板类提供原子操作支持。

原子操作适用于以下场景:

  • 计数器增减
  • 标志位设置
  • 无锁数据结构(Lock-Free Data Structures)

为什么需要原子操作?[编辑 | 编辑源代码]

在多线程环境中,多个线程同时修改同一变量可能导致数据竞争(Data Race)。例如,以下非原子操作可能产生不可预测的结果:

  
#include <iostream>  
#include <thread>  

int counter = 0;  

void increment() {  
    for (int i = 0; i < 100000; ++i) {  
        ++counter; // 非原子操作  
    }  
}  

int main() {  
    std::thread t1(increment);  
    std::thread t2(increment);  
    t1.join();  
    t2.join();  
    std::cout << "Counter: " << counter << std::endl; // 结果可能小于200000  
}

输出示例(错误结果)

  
Counter: 153742  

原因:++counter实际包含“读取-修改-写入”三步,线程切换可能导致中间状态丢失。

C++原子类型[编辑 | 编辑源代码]

C++11引入std::atomic<T>模板类,支持整数、指针和自定义类型(需满足特定条件)。常用原子类型包括:

  • std::atomic<int>
  • std::atomic<bool>
  • std::atomic<size_t>

基本用法[编辑 | 编辑源代码]

修复上述计数问题的原子版本:

  
#include <atomic>  
#include <iostream>  
#include <thread>  

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

void increment() {  
    for (int i = 0; i < 100000; ++i) {  
        ++counter; // 原子操作  
    }  
}  

int main() {  
    std::thread t1(increment);  
    std::thread t2(increment);  
    t1.join();  
    t2.join();  
    std::cout << "Counter: " << counter << std::endl; // 正确输出200000  
}

输出

  
Counter: 200000  

内存顺序(Memory Order)[编辑 | 编辑源代码]

原子操作允许指定内存顺序,控制操作间的可见性。C++提供六种内存顺序,分为三类: 1. 顺序一致(Sequentially Consistent)memory_order_seq_cst 2. 获取-释放(Acquire-Release)memory_order_acquire, memory_order_release, memory_order_acq_rel 3. 松散(Relaxed)memory_order_relaxed

graph LR A[线程A: store X] -->|release| B[内存屏障] B -->|acquire| C[线程B: load X]

示例:使用内存顺序[编辑 | 编辑源代码]

  
#include <atomic>  
#include <thread>  
#include <assert.h>  

std::atomic<bool> x(false), y(false);  
std::atomic<int> z(0);  

void write_x_then_y() {  
    x.store(true, std::memory_order_relaxed);  
    y.store(true, std::memory_order_release); // 保证之前的操作对acquire可见  
}  

void read_y_then_x() {  
    while (!y.load(std::memory_order_acquire)); // 同步release  
    if (x.load(std::memory_order_relaxed))  
        ++z;  
}  

int main() {  
    std::thread a(write_x_then_y);  
    std::thread b(read_y_then_x);  
    a.join();  
    b.join();  
    assert(z.load() != 0); // 永远不会触发  
}

实际应用案例[编辑 | 编辑源代码]

无锁队列[编辑 | 编辑源代码]

原子操作可用于实现高性能无锁队列。以下是一个简化版实现:

  
#include <atomic>  
#include <memory>  

template<typename T>  
class LockFreeQueue {  
private:  
    struct Node {  
        std::shared_ptr<T> data;  
        std::atomic<Node*> next;  
        Node(T const& value) : data(std::make_shared<T>(value)), next(nullptr) {}  
    };  

    std::atomic<Node*> head;  
    std::atomic<Node*> tail;  

public:  
    void push(T const& value) {  
        Node* new_node = new Node(value);  
        Node* old_tail = tail.load();  
        while (!tail.compare_exchange_weak(old_tail, new_node)) {  
            old_tail = tail.load();  
        }  
        old_tail->next = new_node;  
    }  

    std::shared_ptr<T> pop() {  
        Node* old_head = head.load();  
        while (old_head && !head.compare_exchange_weak(old_head, old_head->next.load())) {  
            old_head = head.load();  
        }  
        return old_head ? old_head->data : std::shared_ptr<T>();  
    }  
};

性能考虑[编辑 | 编辑源代码]

原子操作通常比互斥锁更快,但在高争用环境下可能因CPU缓存同步(如MESI协议)导致性能下降。建议:

  • 对高频访问变量使用memory_order_relaxed
  • 避免在原子操作中包含复杂逻辑

总结[编辑 | 编辑源代码]

  • 原子操作通过硬件指令保证操作的不可分割性。
  • std::atomic支持整数、指针和自定义类型。
  • 内存顺序允许开发者平衡性能与一致性需求。
  • 适用于计数器、标志位和无锁数据结构等场景。

模板:C++多线程导航