C++(26): 原子操作(std::atomic)

目录

[1. 简述](#1. 简述)

[2. 什么是原子操作](#2. 什么是原子操作)

[3. C++原子操作](#3. C++原子操作)

[4. std::atomic_flag](#4. std::atomic_flag)

[5. std::atomic](#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宏初始化。

cpp 复制代码
#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,进行访问,之后清除。

cpp 复制代码
#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用来交换新值,返回旧值。

cpp 复制代码
#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等。

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

cpp 复制代码
#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实现比较及交换功能,二者的功能是一样的,但是前者性能更好一些,常在高速循环中使用。

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

a.compare_exchange_weak(b, c)其中a是当前值,b期望值,c新值

a==b时:函数返回真,并把c赋值给a

a!=b时:函数返回假,并把a复制给b

cpp 复制代码
#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
相关推荐
_WndProc12 分钟前
C++ 日志输出
开发语言·c++·算法
薄荷故人_13 分钟前
从零开始的C++之旅——红黑树及其实现
数据结构·c++
m0_7482400213 分钟前
Chromium 中chrome.webRequest扩展接口定义c++
网络·c++·chrome
qq_4335545421 分钟前
C++ 面向对象编程:+号运算符重载,左移运算符重载
开发语言·c++
努力学习编程的伍大侠25 分钟前
基础排序算法
数据结构·c++·算法
yuyanjingtao1 小时前
CCF-GESP 等级考试 2023年9月认证C++四级真题解析
c++·青少年编程·gesp·csp-j/s·编程等级考试
闻缺陷则喜何志丹1 小时前
【C++动态规划 图论】3243. 新增道路查询后的最短距离 I|1567
c++·算法·动态规划·力扣·图论·最短路·路径
charlie1145141912 小时前
C++ STL CookBook
开发语言·c++·stl·c++20
小林熬夜学编程2 小时前
【Linux网络编程】第十四弹---构建功能丰富的HTTP服务器:从状态码处理到服务函数扩展
linux·运维·服务器·c语言·网络·c++·http
倔强的石头1062 小时前
【C++指南】类和对象(九):内部类
开发语言·c++