C++之原子操作:实现高效、安全的多线程编程

发布时间 2023-07-08 11:16:54作者: 冰山奇迹

背景

在多线程编程中,线程间的同步和数据竞争问题是无法避免的。传统的同步方法,如互斥锁(mutex)和条件变量(condition variable),可能导致性能下降和死锁等问题。C++11引入了原子操作,提供了一种更高效、安全的多线程编程方式。本文将介绍C++中的原子操作概念、使用方法及示例。

C++中的原子操作

C++11引入了头文件,提供了一系列原子类型和操作。原子类型包括std::atomic_flag、std::atomic和特化的原子整数类型(如std::atomic_int、std::atomic_long等)。原子操作包括原子读写、原子算术运算、原子位操作等。

原子类型

(1)std::atomic_flag

std::atomic_flag是最简单的原子类型,只有两种状态:设置(set)和清除(clear)。std::atomic_flag不可拷贝和赋值,且必须使用ATOMIC_FLAG_INIT宏初始化。

#include <atomic>

std::atomic_flag flag = ATOMIC_FLAG_INIT;

(2)std::atomic

std::atomic是通用的原子类型,可以用于任何可拷贝的类型T。std::atomic的初始化可以使用默认构造函数、拷贝构造函数或者赋值操作。

#include <atomic>

std::atomic<int> atomic_int(0); // 初始化为0

3)特化的原子整数类型

特化的原子整数类型是针对整数类型的原子类型,如std::atomic_int、std::atomic_long等。它们的使用方法与std::atomic相同。

#include <atomic>

std::atomic_int atomic_int(0); // 初始化为0

原子操作

(1)原子读写 原子读写操作包括load、store和exchange。load用于读取原子变量的值,store用于设置原子变量的值,exchange用于交换原子变量的值。

#include <atomic>
#include <iostream>

int main() {
    std::atomic<int> atomic_int(0);

    int value = atomic_int.load(); // 读取原子变量的值
    std::cout << "value: " << value << std::endl;

    atomic_int.store(10); // 设置原子变量的值
    std::cout << "value: " << atomic_int.load() << std::endl;

    int old_value = atomic_int.exchange(20); // 交换原子变量的值
    std::cout << "old_value: " << old_value << ", new_value: " << atomic_int.load() << std::endl;

    return 0;
}

(2)原子算术运算

原子算术运算包括fetch_add、fetch_sub、fetch_and、fetch_or和fetch_xor等。这些操作可以实现原子加、原子减、原子与、原子或和原子异或等功能。

#include <atomic>
#include <iostream>

int main() {
    std::atomic<int> atomic_int(0);

    int old_value = atomic_int.fetch_add(10); // 原子加操作 
    std::cout << "old_value: " << old_value << ", new_value: " << atomic_int.load() << std::endl;
    old_value = atomic_int.fetch_sub(5); // 原子减操作
std::cout << "old_value: " << old_value << ", new_value: " << atomic_int.load() << std::endl;

old_value = atomic_int.fetch_and(0b1100); // 原子与操作
std::cout << "old_value: " << old_value << ", new_value: " << atomic_int.load() << std::endl;

old_value = atomic_int.fetch_or(0b1010); // 原子或操作
std::cout << "old_value: " << old_value << ", new_value: " << atomic_int.load() << std::endl;

old_value = atomic_int.fetch_xor(0b1111); // 原子异或操作
std::cout << "old_value: " << old_value << ", new_value: " << atomic_int.load() << std::endl;

return 0;
}

(3)原子比较和交换

原子比较和交换操作(compare-and-swap,CAS)是一种常用的同步原语,用于实现无锁数据结构。std::atomic<T>提供了compare_exchange_weakcompare_exchange_strong两个CAS操作。

#include <atomic>
#include <iostream>

int main() {
    std::atomic<int> atomic_int(0);

    int expected = 0;
    bool success = atomic_int.compare_exchange_weak(expected, 10); // CAS操作
    std::cout << "success: " << success << ", expected: " << expected << ", value: " << atomic_int.load() << std::endl;

    expected = 0;
    success = atomic_int.compare_exchange_strong(expected, 20); // CAS操作
    std::cout << "success: " << success << ", expected: " << expected << ", value: " << atomic_int.load() << std::endl;

    return 0;
}

原子操作示例

下面是一个使用原子操作实现的多线程累加器示例。在这个示例中,我们创建了两个线程,分别对原子整数变量atomic_int进行累加操作。

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

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

void add(int n) {
    for (int i = 0; i < n; ++i) {
        atomic_int.fetch_add(1);
    }
}

int main() {
    std::thread t1(add, 100000);
    std::thread t2(add, 100000);

    t1.join();
    t2.join();

    std::cout << "atomic_int: " << atomic_int.load() << std::endl;

    return 0;
}

背景

通过本文的介绍,我们了解了C++中原子操作的概念、使用方法及示例。原子操作为多线程编程提供了一种高效、安全的方式,避免了传统同步方法的性能和死锁问题。在实际编程中,我们可以根据需求选择合适的原子类型和操作,实现无锁数据结构和并发算法。