目录

1. 简述

2. 什么是原子操作

3. C++原子操作

4. std::atomic_flag

5. std::atomic

(1)操作

(2)赋值(store)、读取(load)与交换(exchange)

(3)算术运算

(4)CAS


1. 简述

        开发过多线程、并发编程的小伙伴一定接触过mutex,通过对资源进行加锁和解锁,实现对方问和修改的互斥操作。mutex使用起来很方便,很强大,但也有局限性。频繁地加锁和解锁会造成较大的资源消耗,影像系统的性能。

        与mutex相比,原子(atomic)操作相对灵活和简单。

        注意,这种灵活性在一定程度上是做了某些妥协的。

2. 什么是原子操作

        原子指的是一系列不可被CPU上下文交换的机器指令,这些指令组合在一起就形成了原子操作。

        我们在日常使用的CPU或SOC基本都是多核的情况,当其中某个CPU核心开始运行原子操作时,会先暂停其它CPU内核对内存的操作,保证对资源的独占性,进而保证资源不会被其它CPU内核所干扰,这就是原子操作的通俗解释。

3. C++原子操作

        C++提供了一个模板类型std::atomic<T>来助力实现原子操作,还提供了一些特化的原子类型,例如std::atomic_int、std::atomic_long等。

        此外还提供了std::atomic_flag这一超简单的原子类型,简单到只有设置(set)和清除(clear)两种状态。

4. std::atomic_flag

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

#include <atomic>

std::atomic_flag flag = ATOMIC_FLAG_INIT;

        关于atomic_flag我们只需要掌握两个成员的使用就可以了,他们分别是test_and_set和clear。

        test_and_set用于判断当前变量是否被设置过,如果没有被设置过,则进行设置,并返回false,反之则直接返回true。

        clear用于清除设置的状态。

        如下所示的例程演示了排他性的访问某些资源,创建10个线程,分别访问同一段资源。当test_and_set返回true时,说明有其他某个线程正在访问,因此等待,知道test_and_set返回false,进行访问,之后清除。

#include <iostream>

#include <atomic>

#include <thread>

#include <vector>

#include <sstream>


std::atomic_flag atomic_state = ATOMIC_FLAG_INIT;

std::stringstream stream_info;

void access_stream(int x)

{

    while (atomic_state.test_and_set()); ///< 等待状态被清除

    stream_info << "thread" << x << "access stream" << '\n';

    atomic_state.clear(); ///< 清楚状态

}

int main()

{

    std::vector < std::thread > threads;

    for(int i = 1; i <= 10; ++i){

        threads.push_back(std::thread(access_stream, i));

    }

    for(auto & th:threads){

        th.join();

    }

    std::cout << stream_info.str() << std::endl;;

    return 0;

}

5. std::atomic<T>

        std::atomic<T>作为一个模板,提供了通用的原子类型,也提供了比std::atomic_flag更为灵活和复杂的应用功能。

        std::atomic_int等作为特化的原子类型,是特殊的std::atomic<T>,一般来讲std::atomic_int等价于std::atomic<int>,其他特化类型类似。

(1)操作

        std::atomic提供了赋值、算术运算和比较交换等操作。

(2)赋值(store)、读取(load)与交换(exchange)

        std::tomic提供了store和load接口,分别用来赋值和读取,也提供了exchange用来交换新值,返回旧值。

#include <iostream>

#include <atomic>


int main(int argc, char* argv[])

{

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

    atomic_int.store(10); ///< 设置原子变量的值

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

    int value = atomic_int.load(); ///< 读取原子变量的值

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

    int old_value = atomic_int.exchange(20); // 交换原子变量的值

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

    return 0;

}

(3)算术运算

        std::atomic提供了原子加,原子减等接口,具体包含fetch_add、fetch_sub、fetch_and、fetch_or和fetch_xor等。

        原子算术运算后,都会返回原值。

#include <iostream>

#include <atomic>


int main(int argc, char* argv[])

{

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

    int last_value;

    last_value = atomic_int.fetch_add(10); ///< 原子加操作 

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

    last_value = atomic_int.fetch_sub(5); ///< 原子减操作

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

    last_value = atomic_int.fetch_and(0b1100); ///< 原子与操作

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


    last_value = atomic_int.fetch_or(0b1010); ///< 原子或操作

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

    last_value = atomic_int.fetch_xor(0b1111); ///< 原子异或操作

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

    return 0;

}

(4)CAS

        CAS,Compare and Swap,比较并交换。

        std::atomic提供了compare_exchange_weak和compare_exchange_strong实现比较及交换功能,二者的功能是一样的,但是前者性能更好一些,常在高速循环中使用。

        参数传入期待值与新值,通过比较当前值与期待值的情况进行区别改变。

#include <iostream>

#include <atomic>


int main(int argc, char* argv[])

{

    std::atomic<int> a;

    a.store(10);

    int b=10;

    int c=20;

    std::cout<<"a:"<<a<<std::endl;

    if(a.compare_exchange_weak(b, c)){ ///< a和b值相同,把c的值赋给a

        std::cout<<"a true:"<<a.load()<<std::endl;

    }

    std::cout<<"a:"<<a<<" b:"<<b<<" c:"<<c<<std::endl;

    return 0;

}

>> 运行结果

a: 20 b:10 c:20
06-22 10:59