C++11原子操作:从入门到精通

C++11原子操作:从入门到精通

一、什么是原子操作? 二、为什么需要原子操作? 三、C++11中的头文件 四、基本使用 1. 声明原子变量 2. 基本原子操作 五、内存顺序(Memory Order) 示例:使用内存顺序实现自旋锁 六、原子类型模板 七、实际应用示例 1. 线程安全的计数器 2. 双重检查锁定(Double-Checked Locking) 八、性能考虑 九、常见陷阱 十、总结 一、什么是原子操作?

原子操作(Atomic Operations)是指不可被中断的一个或一系列操作。在多线程编程中,原子操作就像是"不可分割的最小单位",要么完全执行,要么完全不执行,不会出现执行到一半被其他线程打断的情况。

二、为什么需要原子操作?

考虑以下场景:

int counter = 0;

// 线程1

void increment() {

counter++;

}

// 线程2

void decrement() {

counter--;

}

在多线程环境下,counter++ 和 counter-- 这样的操作实际上包含多个步骤(读取、修改、写入),可能导致竞态条件(Race Condition)。原子操作可以确保这些操作不可分割地完成。

三、C++11中的头文件

C++11引入了头文件,提供了一系列原子类型和操作。主要包含:

基本原子类型(atomic_int, atomic_bool等)

类模板std::atomic

原子操作函数

内存顺序控制

四、基本使用

1. 声明原子变量

#include

#include

int main() {

std::atomic counter(0); // 初始化为0

counter.store(10); // 原子存储

int value = counter.load(); // 原子读取

std::cout << "Counter: " << value << std::endl;

return 0;

}

2. 基本原子操作

std::atomic num(0);

// 原子加法

num.fetch_add(5); // 相当于 num += 5

// 原子减法

num.fetch_sub(3); // 相当于 num -= 3

// 原子交换

int old = num.exchange(10); // 将num设为10,返回旧值

// 比较交换(CAS操作)

int expected = 10;

bool success = num.compare_exchange_strong(expected, 15);

// 如果num == expected,则设为15,返回true

// 否则将expected设为num的实际值,返回false

五、内存顺序(Memory Order)

内存顺序指定了原子操作周围的内存访问如何排序,是原子操作的高级特性。C++11定义了6种内存顺序:

memory_order_relaxed:最宽松的顺序,只保证原子性

memory_order_consume:依赖于此原子变量的后续操作不能重排序到它前面

memory_order_acquire:后续操作不能重排序到它前面

memory_order_release:前面操作不能重排序到它后面

memory_order_acq_rel:acquire + release

memory_order_seq_cst:最严格的顺序(默认)

示例:使用内存顺序实现自旋锁

#include

#include

class SpinLock {

std::atomic_flag flag = ATOMIC_FLAG_INIT;

public:

void lock() {

while (flag.test_and_set(std::memory_order_acquire)) {

// 自旋等待

}

}

void unlock() {

flag.clear(std::memory_order_release);

}

};

六、原子类型模板

std::atomic可以用于任何可平凡复制的类型:

struct Point {

int x, y;

};

std::atomic atomic_point;

void updatePoint() {

Point new_point{10, 20};

atomic_point.store(new_point, std::memory_order_release);

}

void readPoint() {

Point current = atomic_point.load(std::memory_order_acquire);

std::cout << "Point: (" << current.x << ", " << current.y << ")\n";

}

七、实际应用示例

1. 线程安全的计数器

#include

#include

#include

#include

std::atomic counter(0);

const int kIncrementsPerThread = 100000;

const int kThreadCount = 10;

void increment() {

for (int i = 0; i < kIncrementsPerThread; ++i) {

counter.fetch_add(1, std::memory_order_relaxed);

}

}

int main() {

std::vector threads;

for (int i = 0; i < kThreadCount; ++i) {

threads.emplace_back(increment);

}

for (auto& t : threads) {

t.join();

}

std::cout << "Final counter value: " << counter << std::endl;

return 0;

}

2. 双重检查锁定(Double-Checked Locking)

class Singleton {

private:

static std::atomic instance;

static std::mutex mutex;

Singleton() {}

public:

static Singleton* getInstance() {

Singleton* tmp = instance.load(std::memory_order_acquire);

if (tmp == nullptr) {

std::lock_guard lock(mutex);

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::instance(nullptr);

std::mutex Singleton::mutex;

八、性能考虑

原子操作比普通操作慢,但比互斥锁快

使用memory_order_relaxed可以获得更好性能(当不需要严格顺序时)

避免过度使用原子变量,考虑无锁数据结构设计

九、常见陷阱

错误地认为原子操作可以替代锁:原子操作只保证单个操作的原子性,复杂操作仍需锁

忽略内存顺序:错误的内存顺序可能导致难以发现的bug

ABA问题:在使用CAS操作时需要注意

十、总结

C++11的原子操作提供了:

线程安全的基本操作

多种内存顺序控制

比锁更高效的并发控制方式

构建无锁数据结构的基础

掌握原子操作是成为高级C++开发者的重要一步。建议从简单场景开始实践,逐步理解更复杂的内存顺序概念。

相关内容

中洲世界下载
365bet官网手机版

中洲世界下载

06-29 ☯ 6816
如何在Windows 10上复制屏幕?详细方法分步教程
田蚡是什么人?他最后的结局怎么样?
365bet账号被限制

田蚡是什么人?他最后的结局怎么样?

06-27 ☯ 5523