0

0

C++原子变量使用 无锁编程实现方法

P粉602998670

P粉602998670

发布时间:2025-08-20 12:10:02

|

631人浏览过

|

来源于php中文网

原创

原子变量通过std::atomic实现无锁编程,提升多线程性能,适用于简单操作,需注意ABA问题、伪共享及内存顺序选择,相比互斥锁性能更高但适用范围有限。

c++原子变量使用 无锁编程实现方法

原子变量在C++中主要用于无锁编程,它允许你在多线程环境中安全地修改变量,而无需显式使用互斥锁。这可以显著提高性能,尤其是在锁竞争激烈的情况下。

解决方案

C++11引入了

头文件,提供了
std::atomic
模板类,用于创建原子变量。以下是使用原子变量实现无锁编程的基本方法:

  1. 包含头文件:

    #include 

    立即学习C++免费学习笔记(深入)”;

  2. 声明原子变量: 使用

    std::atomic
    声明原子变量,其中
    T
    是变量的类型 (例如
    int
    ,
    bool
    ,
    double
    , 指针等)。

    std::atomic counter(0); // 初始化为0
    std::atomic flag(false);
  3. 原子操作: 使用原子操作来修改和读取原子变量。

    std::atomic
    提供了以下常用的原子操作:

    • load()
      : 原子地读取变量的值。
    • store(value)
      : 原子地存储新的值到变量。
    • exchange(value)
      : 原子地将变量的值替换为新的值,并返回旧的值。
    • compare_exchange_weak(expected, desired)
      : 如果变量的当前值等于
      expected
      ,则原子地将变量的值替换为
      desired
      。 如果不相等,则将
      expected
      更新为变量的当前值。 这是一个弱版本,可能虚假地失败(即使变量的值等于
      expected
      ),因此通常需要在一个循环中使用。
    • compare_exchange_strong(expected, desired)
      : 与
      compare_exchange_weak
      类似,但保证不会虚假地失败。 性能可能比
      compare_exchange_weak
      差。
    • fetch_add(value)
      : 原子地将变量的值加上
      value
      ,并返回旧的值。
    • fetch_sub(value)
      : 原子地将变量的值减去
      value
      ,并返回旧的值。
    • fetch_and(value)
      : 原子地将变量的值与
      value
      进行按位与运算,并返回旧的值。
    • fetch_or(value)
      : 原子地将变量的值与
      value
      进行按位或运算,并返回旧的值。
    • fetch_xor(value)
      : 原子地将变量的值与
      value
      进行按位异或运算,并返回旧的值。
    counter.store(10);
    int oldValue = counter.load();
    int exchangedValue = counter.exchange(20); // oldValue 现在是 10, counter 现在是 20
    
    int expected = 20;
    int desired = 30;
    while (!counter.compare_exchange_weak(expected, desired)) {
        // compare_exchange_weak 可能失败,即使 counter 的值等于 expected
        // 在循环中重试,更新 expected 的值
    }
    // counter 现在是 30 (如果 compare_exchange_weak 成功)
  4. 内存顺序: 原子操作可以指定内存顺序,控制不同线程之间内存访问的可见性。 常用的内存顺序包括:

    • std::memory_order_relaxed
      : 最宽松的内存顺序,只保证原子性,不保证线程之间的同步。
    • std::memory_order_acquire
      : 当线程读取原子变量时,确保该线程能看到其他线程在之前写入原子变量的所有操作。
    • std::memory_order_release
      : 当线程写入原子变量时,确保该线程之前的所有操作对其他线程可见。
    • std::memory_order_acq_rel
      : 同时具有
      acquire
      release
      语义。 用于读-修改-写操作。
    • std::memory_order_seq_cst
      : 最强的内存顺序,提供顺序一致性,保证所有线程以相同的顺序看到所有原子操作。 这是默认的内存顺序,但性能通常最差。
    std::atomic data(0);
    std::atomic ready(false);
    
    // 线程 1
    void producer() {
        data.store(42, std::memory_order_relaxed);
        ready.store(true, std::memory_order_release);
    }
    
    // 线程 2
    void consumer() {
        while (!ready.load(std::memory_order_acquire)) {
            // 等待 ready 变为 true
        }
        int value = data.load(std::memory_order_relaxed);
        // 使用 value
    }

副标题1:原子变量有哪些常见的陷阱和误用?

原子变量的使用并非万无一失,存在一些常见的陷阱,尤其是在处理复杂的并发逻辑时。

  • ABA问题: 如果一个原子变量的值从A变为B,然后又变回A,那么

    compare_exchange
    操作可能会成功,即使实际上变量的值已经发生了变化。 解决ABA问题可以使用版本号或者指针来跟踪变量的变化。例如,使用
    std::atomic>
    ,其中
    int
    是版本号。

  • 伪共享: 如果多个线程访问位于同一缓存行的不同原子变量,即使这些变量本身是原子操作的,也会导致性能下降。 这是因为缓存行需要在不同的核心之间来回传递。 可以使用填充 (padding) 来确保每个原子变量都位于不同的缓存行中。

    alignas(64)
    可以用来对齐变量到缓存行大小。

  • 过度使用原子操作: 原子操作通常比非原子操作慢。 过度使用原子操作可能会降低性能。 只在必要的时候使用原子操作,并尽可能使用更宽松的内存顺序。

  • 死锁: 虽然原子操作本身不会导致死锁,但在复杂的并发逻辑中,如果原子操作与其他同步机制(例如互斥锁)混合使用,仍然可能发生死锁。

  • 忽略内存顺序: 错误地选择内存顺序可能会导致数据竞争和未定义的行为。 仔细考虑不同线程之间的数据依赖关系,并选择合适的内存顺序。 通常,

    std::memory_order_seq_cst
    是最安全的,但也是性能最差的。

    Lifetoon
    Lifetoon

    免费的AI漫画创作平台

    下载

副标题2:如何选择合适的内存顺序?

选择合适的内存顺序需要仔细考虑不同线程之间的数据依赖关系。以下是一些选择内存顺序的指导原则:

  • 没有数据依赖关系: 如果多个线程之间没有数据依赖关系,可以使用

    std::memory_order_relaxed
    。 例如,多个线程同时增加一个计数器,而不需要保证线程之间的同步。

  • 生产者-消费者模式: 在生产者-消费者模式中,生产者线程写入数据,消费者线程读取数据。 生产者线程应该使用

    std::memory_order_release
    来发布数据,消费者线程应该使用
    std::memory_order_acquire
    来获取数据。

  • 读-修改-写操作: 对于读-修改-写操作,应该使用

    std::memory_order_acq_rel
    。 例如,原子地增加一个计数器,并返回旧的值。

  • 顺序一致性: 如果需要保证所有线程以相同的顺序看到所有原子操作,可以使用

    std::memory_order_seq_cst
    。 但是,这通常会降低性能。

在实际应用中,通常需要根据具体的并发场景进行权衡。 可以使用工具(例如 Valgrind 的 Helgrind)来检测数据竞争和内存顺序错误。

副标题3:原子变量和互斥锁相比,有哪些优缺点?

原子变量和互斥锁都是用于保护共享资源的同步机制,但它们有不同的优缺点:

原子变量的优点:

  • 性能更高: 原子操作通常比互斥锁更快,尤其是在锁竞争激烈的情况下。 这是因为原子操作通常是由硬件直接支持的,而互斥锁需要操作系统内核的介入。
  • 避免死锁: 原子操作本身不会导致死锁。
  • 更细粒度的控制: 原子操作可以提供更细粒度的控制,允许对单个变量进行原子操作,而无需锁定整个代码块。

原子变量的缺点:

  • 适用范围有限: 原子操作只能用于简单的操作,例如读取、写入、增加、减少等。 对于复杂的操作,仍然需要使用互斥锁。
  • 更难理解和调试: 原子操作的内存顺序和并发语义比互斥锁更难理解和调试。
  • ABA问题: 原子操作可能受到ABA问题的影响。

互斥锁的优点:

  • 适用范围更广: 互斥锁可以用于保护任意类型的共享资源,包括复杂的数据结构和代码块。
  • 更易于理解和调试: 互斥锁的并发语义比原子操作更易于理解和调试。
  • 避免ABA问题: 互斥锁可以避免ABA问题。

互斥锁的缺点:

  • 性能较低: 互斥锁的性能通常比原子操作更低,尤其是在锁竞争激烈的情况下。
  • 可能导致死锁: 互斥锁可能导致死锁。
  • 锁定粒度较粗: 互斥锁的锁定粒度通常较粗,可能会降低并发性。

总的来说,原子变量更适合于简单的、高性能的并发场景,而互斥锁更适合于复杂的、需要保护任意类型共享资源的并发场景。 在实际应用中,通常需要根据具体的场景进行选择。有时,原子变量和互斥锁可以结合使用,以达到最佳的性能和安全性。

相关专题

更多
string转int
string转int

在编程中,我们经常会遇到需要将字符串(str)转换为整数(int)的情况。这可能是因为我们需要对字符串进行数值计算,或者需要将用户输入的字符串转换为整数进行处理。php中文网给大家带来了相关的教程以及文章,欢迎大家前来学习阅读。

312

2023.08.02

int占多少字节
int占多少字节

int占4个字节,意味着一个int变量可以存储范围在-2,147,483,648到2,147,483,647之间的整数值,在某些情况下也可能是2个字节或8个字节,int是一种常用的数据类型,用于表示整数,需要根据具体情况选择合适的数据类型,以确保程序的正确性和性能。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

522

2024.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

49

2025.08.29

C++中int的含义
C++中int的含义

本专题整合了C++中int相关内容,阅读专题下面的文章了解更多详细内容。

190

2025.08.29

c++怎么把double转成int
c++怎么把double转成int

本专题整合了 c++ double相关教程,阅读专题下面的文章了解更多详细内容。

49

2025.08.29

C++中int、float和double的区别
C++中int、float和double的区别

本专题整合了c++中int和double的区别,阅读专题下面的文章了解更多详细内容。

95

2025.10.23

treenode的用法
treenode的用法

​在计算机编程领域,TreeNode是一种常见的数据结构,通常用于构建树形结构。在不同的编程语言中,TreeNode可能有不同的实现方式和用法,通常用于表示树的节点信息。更多关于treenode相关问题详情请看本专题下面的文章。php中文网欢迎大家前来学习。

529

2023.12.01

C++ 高效算法与数据结构
C++ 高效算法与数据结构

本专题讲解 C++ 中常用算法与数据结构的实现与优化,涵盖排序算法(快速排序、归并排序)、查找算法、图算法、动态规划、贪心算法等,并结合实际案例分析如何选择最优算法来提高程序效率。通过深入理解数据结构(链表、树、堆、哈希表等),帮助开发者提升 在复杂应用中的算法设计与性能优化能力。

7

2025.12.22

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

65

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
最新Python教程 从入门到精通
最新Python教程 从入门到精通

共4课时 | 0.6万人学习

Rust 教程
Rust 教程

共28课时 | 4万人学习

Kotlin 教程
Kotlin 教程

共23课时 | 2.2万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号