【C++】atomic原子操作

std::atomic


C++中原子变量(atomic)是一种多线程编程中常用的同步机制,它能够确保对共享变量的操作在执行时不会被其他线程的操作干扰,从而避免竞态条件(race condition)和死锁(deadlock)等问题。
C++11起提供了atomic,可以使用它定义一个原子类型。

template< class T >
struct atomic;

构造函数

std::atomic::atomic

std::atomic<bool> ready (false);
std::atomic<int> a;

is_lock_free函数

is_lock_free函数是一个成员函数,用于检查当前atomic对象是否支持无锁操作。调用此成员函数不会启动任何数据竞争

bool is_lock_free() const volatile noexcept;
bool is_lock_free() const noexcept;

std::atomic_flag

std::atomic_flag是 C++ 中的一个原子布尔类型,它用于实现原子锁操作。

  1. std::atomic_flag 默认是清除状态(false)。可以使用 ATOMIC_FLAG_INIT 宏进行初始化,例如:std::atomic_flag flag = ATOMIC_FLAG_INIT;
  2. std::atomic_flag 提供了两个成员函数 test_and_set() 和 clear() 来测试和设置标志位。test_and_set() 函数会将标志位置为 true,并返回之前的值;clear() 函数将标志位置为 false。
  3. std::atomic_flag 的 test_and_set() 和 clear() 操作是原子的,可以保证在多线程环境下正确执行。
  4. std::atomic_flag 只能表示两种状态,即 true 或 false,不能做其他比较操作。通常情况下,std::atomic_flag 被用作简单的互斥锁,而不是用来存储信息。

atomic_flag实现原子锁

//原子锁
class CASLock : Noncopyable {
public:
    typedef ScopedLockImpl<CASLock> Lock;

    CASLock(){
        m_mutex.clear();
    }
    ~CASLock(){

    }
    void lock(){
        while(std::atomic_flag_test_and_set_explicit(&m_mutex , std::memory_order_acquire));
    }
    void unlick(){
        std::atomic_flag_clear_explicit(&m_mutex , std::memory_order_release);
    }
private:
    // 原子状态
    volatile std::atomic_flag m_mutex;
};

atomic_flag实现自旋锁

atomic_flag lock = ATOMIC_FLAG_INIT;// 设置为false状态

void f(int n){
    // test_and_set会不断设置新值并返回旧值
    while (lock.test_and_set(std::memory_order_acquire))
        cout << "Waiting from thread " << n << endl;
    cout << "Thread" << n << " starts working" << endl;
}

void g(int n) {
    cout << "Thread " << n << " is going to start" << endl;
    lock.clear();// 将lock设置false
    cout << "Thread " << n << " starts working" << endl;
}

void test() {
    lock.test_and_set();
    thread t1(f, 1);
    thread t2(g, 2);

    t1.join();
    sleep(5);
    t2.join();
}
// 当t1执行后,lock为true,则一直自旋等待,直到t2加入后,clear将其设置为false,t1才终止自旋,执行后续代码;

store函数

void store(T desired, std::memory_order order = std::memory_order_seq_cst) volatile noexcept;
void store(T desired, std::memory_order order = std::memory_order_seq_cst) noexcept;

desired:要存储的值。
order:存储操作的内存顺序。默认是std::memory_order_seq_cst

#include <iostream>
#include <atomic>

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

    int val = 10;
    atomic_int.store(val);

    std::cout << "Value stored in atomic object: " << atomic_int << std::endl;

    return 0;
}

//Value stored in atomic object: 10

内存模型

【硬件内存模型】
当执行按照一定的顺序执行,则该内存模型为强顺序;
当执行不一定按照顺序执行,则该模型为弱顺序;
在多线程中,当线程间的内存数据被改变的顺序与机器指令中声明的不一致,则为弱顺序;
而上述的原子操作时强顺序的;

- 弱顺序的指令执行性能一般较高,当没有要求需要顺序执行时,使用该方式可以给程序提高性能;

【C++内存模型】
- 编译器保证原子操作间的指令顺序不变;
- 处理器对原子操作的汇编指令的执行顺序不变;
部分平台会阻止编译器优化,加入内存栅栏来保证atomic的顺序一致性,将会大大影响性能;

【如何解决不阻止编译器优化】:让程序为原子操作指定内存顺序(memory_order);
能够让编译器重排序或处理器乱序执行;
memory_order_relaxed    :不对执行顺序做任何保证;
memory_order_acquire    :本线程中,所有后续的读操作必须再本条原子操作完成后执行;
memory_order_release    :本线程中,所有之前的写操作完成后才能执行本条原子操作;
memory_order_acq_rel    :同时包含memory_order_acquire、memory_order_release;
memory_order_consume    :本线程中,所有后续的有关原子类型操作,后再本条原子操作完成后执行;
memory_order_seq_cst    :全部存取都按顺序执行(可能会导致性能损失);

- 上述中,可以使用memory_order_release和memory_order_consume产生;

load函数

load函数用于获取原子变量的当前值。

T load(memory_order order = memory_order_seq_cst) const noexcept;
operator T() const noexcept;

load函数的参数memory_order表示内存序,也就是对原子变量的读操作要遵循哪种内存模型。C++中定义了多种内存序,包括:

  • memory_order_relaxed:最轻量级的内存序,不提供任何同步机制。
  • memory_order_acquire:在本线程中,所有后面的读写操作必须在这个操作之后执行。
  • memory_order_release:在本线程中,该操作之前的所有读写操作必须在这个操作之前执行。
  • memory_order_seq_cst:最严格的内存序,保证所有线程看到的读写操作的顺序都是一致的。

使用load函数时,如果不指定memory_order,则默认为memory_order_seq_cst。

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

int x;
do {
    x = foo.load(std::memory_order_relaxed);  // get value atomically
} while (x==0);

exchange函数

访问和修改包含的值,将包含的值替换并返回它前面的值。

template< class T >
T exchange( volatile std::atomic<T>* obj, T desired );

示例

// atomic::load/store example
#include <iostream> // std::cout
#include <atomic> // std::atomic, std::memory_order_relaxed
#include <thread> // std::thread
//std::atomic<int> count = 0;//错误初始化
std::atomic<int> count(0); // 准确初始化
void set_count(int x)
{
    std::cout << "set_count:" << x << std::endl;
    count.store(x, std::memory_order_relaxed); // set value atomically
}
void print_count()
{
    int x;
    do {
        x = count.load(std::memory_order_relaxed); // get value atomically
    } while (x==0);
    std::cout << "count: " << x << '\n';
}
int main ()
{
    std::thread t1 (print_count);
    std::thread t2 (set_count, 10);
    t1.join();
    t2.join();
    std::cout << "main finish\n";
    return 0;
}

  • 46
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 9
    评论
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值