加载和存储操作是 std::atomic
加载操作
加载操作是通过 load 成员函数实现的,它用于从原子变量中读取一个值。这个函数可以确保在读取过程中,不会被其他线程打断。
std::atomic<int> atomic_var(10); // 初始化一个原子变量,初始值为 10
int value = atomic_var.load(); // 原子加载操作,将 atomic_var 的值加载到 value 中
存储操作
存储操作是通过 store 成员函数实现的,它用于向原子变量中写入一个值。这个函数可以确保在写入过程中,不会被其他线程打断。
std::atomic<int> atomic_var(10); // 初始化一个原子变量,初始值为 10
atomic_var.store(20); // 原子存储操作,将 20 存储到 atomic_var 中
示例
以下是一个综合示例,展示如何使用这两个函数安全地在多个线程之间共享和更新一个原子整数。
#include
#include
#include
#include
std::atomic<int> shared_data(0); // 初始化一个原子整数为0
void increment(int n) {
for (int i = 0; i < n; ++i) {
shared_data.store(shared_data.load() + 1); // 原子加载,增加,然后存储
}
}
void decrement(int n) {
for (int i = 0; i < n; ++i) {
int value = shared_data.load(); // 原子加载当前值
while (!shared_data.compare_exchange_weak(value, value - 1)) {
// compare_exchange_weak 是一个原子比较和交换操作
// 如果当前值与预期的 value 相等,则将其替换为 value - 1
// 如果不相等,说明其他线程已经修改了值,重新加载并尝试
}
}
}
int main()
{
const int num_threads = 4;
const int increment_amount = 100;
const int decrement_amount = 50;
std::vector<std::thread> threads;
// 创建两个增加线程和两个减少线程
for (int i = 0; i < 2; ++i) {
threads.emplace_back(increment, increment_amount);
}
for (int i = 0; i < 2; ++i) {
threads.emplace_back(decrement, decrement_amount);
}
// 等待所有线程完成
for (auto& thread : threads) {
thread.join();
}
int final_value = shared_data.load(); // 原子加载最终值
std::cout << "Final value of shared_data: " << final_value << std::endl;
return 0;
}
上面代码的输出为:
Final value of shared_data: 100
在这个示例中:
注意,compare_exchange_weak 是一种更强大的原子操作,它通常用于实现更复杂的并发控制逻辑,如锁和无锁数据结构。在简单的增加和减少操作中,直接使用 load 和 store 也是可以的,但 compare_exchange_weak 可以提供更强大的保证,特别是在高并发环境下。
std::atomic
std::atomic
T exchange( T desired );
其中 desired 是需要与 std::atomic
这个过程是原子的,即它在多线程环境中是线程安全的,不会有其他线程能够在这个操作执行期间改变 std::atomic
下面是一个使用 std::atomic
#include
#include
#include
#include
std::atomic<int> counter(0); // 初始化原子计数器为0
void incrementAndExchange(int incrementBy, int exchangeWith) {
int oldValue = counter.exchange(exchangeWith); // 交换并获取旧值
oldValue += incrementBy; // 在这里可以对旧值进行操作,但这不会影响到原子对象
std::cout << "Thread " << std::this_thread::get_id() << " exchanged " << oldValue
<< " with " << exchangeWith << std::endl;
}
int main()
{
const int numThreads = 4;
std::vector<std::thread> threads;
// 创建四个线程,每个线程都会尝试交换计数器的值
for (int i = 0; i < numThreads; ++i) {
int increment = (i + 1) * 10; // 每个线程增加的值不同
int newValue = i * 100; // 每个线程想要交换的新值也不同
threads.emplace_back(incrementAndExchange, increment, newValue);
}
// 等待所有线程完成
for (auto& thread : threads) {
thread.join();
}
int finalValue = counter.load(); // 原子加载最终值
std::cout << "Final value of counter: " << finalValue << std::endl;
return 0;
}
上面代码的输出为:
Thread 10308 exchanged 10 with 0
Thread 11188 exchanged 130 with 200
Thread 9836 exchanged 240 with 300
Thread 7020 exchanged 20 with 100
Final value of counter: 300
在这个示例中:
注意:由于多个线程同时尝试交换 counter 的值,因此最终值将取决于操作系统调度线程的顺序以及 exchange 操作的时机。因此,每次运行程序时,最终值都可能不同。
std::atomic
std::atomic::compare_exchange_strong
compare_exchange_strong 函数执行一个“比较并交换”操作,如果当前值与预期值(期望的旧值)相等,则原子地将该值更新为新值。它提供了一种强一致性的内存模型保证,确保不会出现虚假失败的情况。
函数的原型如下:
bool compare_exchange_strong( T& expected, T desired );
expected 是一个引用,用于存储当前 std::atomic
desired 是想要设置的新值。
如果 std::atomi\c 对象的当前值与 expected 相等,则对象的值会被设置为 desired,并且 expected 会被更新为对象的新值(即 desired)。函数返回 true 表示交换成功。如果当前值与 expected 不相等,则对象的值不会被改变,expected 会被更新为对象的当前值,函数返回 false。
std::atomic::compare_exchange_weak
compare_exchange_weak 函数与 compare_exchange_strong 功能相似,也是执行一个“比较并交换”操作。但是,compare_exchange_weak 提供了一种弱一致性的内存模型保证,它可能在某些情况下返回失败,即使当前值与预期值相等(即所谓的“虚假失败”)。这种“弱”版本在某些场景下可能性能更好,但使用它需要更小心,因为需要准备重新尝试操作直到成功。
函数的原型如下:
bool compare_exchange_weak( T& expected, T desired );
其参数和返回值与 compare_exchange_strong 相同。
综合示例
下面是一个使用 std::atomic
#include
#include
#include
#include
#include
// 自旋锁的实现,使用std::atomic和compare_exchange_weak
class Spinlock {
public:
void lock() {
bool expected = false;
while (!locked_.compare_exchange_weak(expected, true)) {
// 如果锁被占用,则继续等待
expected = false; // 重置expected值,准备下一次比较交换
std::this_thread::yield(); // 可以让出CPU时间片
}
}
void unlock() {
locked_.store(false, std::memory_order_release);
}
private:
std::atomic<bool> locked_{ false };
};
// 临界区,需要保护的共享资源
int shared_resource = 0;
Spinlock spinlock;
void increment_shared_resource(int n) {
spinlock.lock();
for (int i = 0; i < n; ++i) {
++shared_resource;
}
spinlock.unlock();
}
int main()
{
const int numThreads = 4;
const int incrementAmount = 100000;
std::vector<std::thread> threads;
// 创建多个线程来增加共享资源
for (int i = 0; i < numThreads; ++i) {
threads.emplace_back(increment_shared_resource, incrementAmount);
}
// 等待所有线程完成
for (auto& thread : threads) {
thread.join();
}
// 输出共享资源的最终值
std::cout << "Final value of shared_resource: " << shared_resource << std::endl;
return 0;
}
上面代码的输出为:
Final value of shared_resource: 400000
在这个示例中:
这个示例展示了如何使用 std::atomic::compare_exchange_weak 来实现一个简单的自旋锁。与 std::atomic_flag 相比,std::atomic 提供了更多的灵活性和控制,但通常 std::atomic_flag 在实现自旋锁时会有更好的性能,因为它专为测试-和-设置操作而设计,且不涉及内存排序的额外开销。在大多数情况下,std::atomic_flag 是自旋锁的首选。然而,了解如何使用 std::atomic::compare_exchange_weak 或 std::atomic::compare_exchange_strong 也是很有价值的,因为它们可以用于实现更复杂的同步原语或算法。
std::atomic
下面是一个综合示例,演示了如何使用 std::atomic
#include
#include
#include
#include
// 声明一个原子整数
std::atomic<int> atomicInt(0);
// 一个函数,用于在多个线程中增加 atomicInt 的值
void incrementAtomicInt(int n) {
for (int i = 0; i < n; ++i) {
// 使用 fetch_add 进行原子加法操作
int oldValue = atomicInt.fetch_add(1);
}
}
int main()
{
const int numThreads = 4;
const int incrementAmount = 10;
std::vector<std::thread> threads;
// 创建多个线程来执行 incrementAtomicInt 函数
for (int i = 0; i < numThreads; ++i) {
threads.emplace_back(incrementAtomicInt, incrementAmount);
}
// 等待所有线程完成
for (auto& thread : threads) {
thread.join();
}
// 输出 atomicInt 的最终值
std::cout << "Final value of atomicInt: " << atomicInt << std::endl;
return 0;
}
上面代码的输出为:
Final value of atomicInt: 40
在这个示例中:
除了原子算术操作外,std::atomic
std::atomic
下面是一个综合示例,展示了如何使用 std::atomic
#include
#include
#include
#include
#include
// 声明一个原子整数
std::atomic<int> atomicInt(0b1010); // 二进制表示,初始值为 1010
// 一个函数,用于在多个线程中修改 atomicInt 的值,通过原子位操作
void modifyAtomicInt(int mask) {
// 使用 fetch_and 执行原子位与操作
int oldValue = atomicInt.fetch_and(mask);
// 使用 fetch_or 执行原子位或操作
oldValue = atomicInt.fetch_or(~mask); // ~mask 是 mask 的位取反
}
int main()
{
const int numThreads = 4;
const int mask = 0b0101; // 二进制掩码
std::vector<std::thread> threads;
// 创建多个线程来执行 modifyAtomicInt 函数
for (int i = 0; i < numThreads; ++i) {
threads.emplace_back(modifyAtomicInt, mask);
}
// 等待所有线程完成
for (auto& thread : threads) {
thread.join();
}
// 输出 atomicInt 的最终值
std::cout << "Final value of atomicInt: " << std::bitset<32>(atomicInt) << std::endl;
return 0;
}
上面代码的输出为:
Final value of atomicInt: 11111111111111111111111111111010
在这个示例中: