c++原子操作 std::atomic

发布于:2024-05-11 ⋅ 阅读:(31) ⋅ 点赞:(0)

1.概要 

std::atomic 是 C++11 标准库引入的一个模板类,用于表示一个原子类型。原子类型是一种特殊的类型,其对象的操作(如读取、写入、修改等)在多线程环境中是原子的,即这些操作是不可中断的,并且在多线程之间是不可见的。换句话说,对原子类型的操作是线程安全的。

原子操作通常用于多线程编程中的同步和通信,因为它们可以确保在多线程环境中对共享数据的访问是安全的,从而避免了数据竞争(data race)和其他并发问题。

std::atomic 模板类提供了多种成员函数和运算符重载,用于执行原子操作,例如:

  • load():从原子对象中加载值。
  • store():将值存储到原子对象中。
  • exchange():以原子方式将新值存储在原子对象中,并返回旧值。
  • compare_exchange_weak() 和 compare_exchange_strong():以原子方式比较并交换值。
  • 运算符重载,如 ++--+=-= 等,用于执行原子算术运算。

使用 std::atomic 可以大大简化多线程编程中的同步工作,因为你可以直接对原子类型进行操作,而无需显式使用互斥体(mutexes)、条件变量(condition variables)或其他同步机制。然而,你仍然需要注意确保逻辑上的正确性,以避免出现死锁、饥饿或其他并发问题。

下面是一个简单的示例,展示了如何使用 std::atomic 来实现一个线程安全的计数器:

#include <iostream>  
#include <thread>  
#include <atomic>  
  
std::atomic<int> counter(0); // 初始化一个原子计数器为 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 << "Final counter value: " << counter << std::endl; // 输出最终的计数器值  
    return 0;  
}

在这个示例中,两个线程都试图递增同一个计数器。由于 counter 是一个 std::atomic<int> 类型,因此这些递增操作是原子的,从而避免了数据竞争。最终,计数器的值将是 200000(假设没有其他并发问题)。

2.std::atomic 多种使用方式举例

std::atomic 是 C++ 中的一个模板类,它提供了原子操作,使得在多线程环境中对数据的访问是线程安全的。以下是 std::atomic 的一些常见使用方式的例子:

1. 基本操作

加载和存储值
#include <iostream>  
#include <atomic>  
  
std::atomic<int> ai(0); // 初始化一个原子整数为 0  
  
int main() {  
    int value = ai.load(); // 加载值  
    ai.store(42); // 存储值  
    std::cout << "Value loaded: " << value << std::endl;  
    std::cout << "Value stored: " << ai.load() << std::endl;  
    return 0;  
}
原子算术操作

#include <iostream>  
#include <atomic>  
  
std::atomic<int> ai(0);  
  
int main() {  
    ai += 10; // 原子加法  
    std::cout << "Value after increment: " << ai.load() << std::endl;  
    ai -= 5; // 原子减法  
    std::cout << "Value after decrement: " << ai.load() << std::endl;  
    return 0;  
}

2. 复合操作

compare_exchange_weak 和 compare_exchange_strong

这两个函数尝试用新值替换当前值,但仅当当前值等于期望值时才这样做。

#include <iostream>  
#include <atomic>  
  
std::atomic<int> ai(0);  
  
int main() {  
    int expected = 0;  
    bool result = ai.compare_exchange_strong(expected, 42); // 尝试替换为 42,如果当前值是 0  
    if (result) {  
        std::cout << "Value replaced successfully" << std::endl;  
    } else {  
        std::cout << "Value not replaced" << std::endl;  
    }  
    std::cout << "Final value: " << ai.load() << std::endl;  
    return 0;  
}

3. 使用 std::atomic 进行线程同步

以下是一个简单的例子,展示了如何使用 std::atomic 和 std::thread 进行线程同步。

#include <iostream>  
#include <thread>  
#include <atomic>  
  
std::atomic<bool> ready(false);  
std::atomic<int> result(0);  
  
void compute() {  
    // 模拟计算过程  
    for (int i = 0; i < 1000000; ++i) {  
        // ...  
    }  
    result.store(42); // 将结果存储在原子变量中  
    ready.store(true); // 设置 ready 标志为 true  
}  
  
int main() {  
    std::thread t(compute);  
  
    // 等待 ready 标志变为 true  
    while (!ready.load()) {  
        // 等待,或者可以做一些其他工作  
    }  
  
    // 当 ready 为 true 时,可以安全地读取 result  
    std::cout << "Result: " << result.load() << std::endl;  
  
    t.join();  
    return 0;  
}

4. 使用 std::atomic 指针

你也可以使用 std::atomic 来存储和管理指针。这在某些情况下(如线程安全的智能指针)可能是有用的。

#include <iostream>  
#include <memory>  
#include <atomic>  
#include <thread>  
  
std::atomic<std::shared_ptr<int>> ptr(nullptr);  
  
void threadFunc() {  
    // 等待 ptr 被设置  
    while (!ptr.load()) {  
        // 等待,或者可以做一些其他工作  
    }  
    std::cout << "Value: " << *ptr.load() << std::endl;  
}  
  
int main() {  
    std::shared_ptr<int> newPtr = std::make_shared<int>(42);  
    ptr.store(newPtr); // 设置原子指针  
  
    std::thread t(threadFunc);  
  
    t.join();  
    return 0;  
}

请注意,虽然 std::atomic 提供了基本的原子操作,但在复杂的并发编程场景中,可能还需要使用其他同步原语(如互斥锁、条件变量等)来确保逻辑的正确性。


网站公告

今日签到

点亮在社区的每一天
去签到