C++之std::atomic解决多线程7个问题(二百四)

news2024/11/22 15:05:02

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长!

优质专栏:Audio工程师进阶系列原创干货持续更新中……】🚀

人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药.

更多原创,欢迎关注:Android系统攻城狮

欢迎关注Android系统攻城狮

1.前言

本篇目的:理解C++之std::atomic原子操作解决多线程竟态问题用法。

2.std::atomic可以解决的C++多线程问题

在C++中,std::atomic提供了一种线程安全的访问原子类型的机制,可以解决以下七个多线程之间的问题:

  1. 竞态条件(Race Condition):多个线程同时访问和修改同一个变量时可能导致数据错误。
  2. 内存可见性(Memory Visibility):不同线程对共享变量的修改可能不可见,导致读取到过期的值。
  3. 乱序执行(Out-of-Order Execution):处理器可能以不同的顺序执行指令,导致结果的不确定性。
  4. 死锁(Deadlock):多个线程相互等待对方释放资源,导致程序无法继续执行。
  5. 活锁(Live Lock):多个线程相互响应对方的动作,而无法继续向前推进。
  6. 数据竞争(Data Race):多个线程同时访问和修改共享数据,没有同步机制可能导致不确定的行为。
  7. 优先级反转(Priority Inversion):高优先级任务被低优先级任务持续占用共享资源,导致高优先级任务无法及时执行。

3.应用实例

  1. 竞态条件(Race Condition):

v1.0 未使用atomic原子操作

#include <atomic>
#include <thread>
#include <cstdio>

//std::atomic<int> counter(0); // 原子变量
int counter = 0; // 普通变量

void increment() {
    for (int i = 0; i < 1000; i++) {
        counter++; // 原子操作:递增
	printf("counter = %d\n",counter);//打印出来的数据是乱序的
    }
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);

    t1.join();
    t2.join();
    return 0;
}

v2.0 使用atomic原子操作

#include <atomic>
#include <thread>

std::atomic<int> counter(0); // 原子变量

void increment() {
    for (int i = 0; i < 1000; i++) {
        counter++; // 原子操作:递增
        printf("counter = %d\n",counter.load());//按顺序打印0 - 2000.	
    }
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);

    t1.join();
    t2.join();
    return 0;
}

多个线程同时对counter进行递增操作,使用std::atomic可以避免竞态条件,保证递增操作的原子性。

  1. 内存可见性(Memory Visibility):
#include <atomic>
#include <thread>
#include <iostream>

std::atomic<bool> flag(false); // 原子标志

void run() {
  while (!flag.load()) {
    // do something
  }


  printf("flag = %d\n",flag.load());
}

int main() {
  std::thread t(run);

  // 假设这里执行一些耗时的计算
  std::this_thread::sleep_for(std::chrono::seconds(1));

  flag.store(true); // 原子操作:标志位置为true

  t.join();

  return 0;
}

flag作为一个标志位,在一个线程中修改为true,其他线程可以通过读取flag的原子操作来感知到修改,保证内存的可见性。

  1. 乱序执行(Out-of-Order Execution):
#include <atomic>
#include <thread>
#include <iostream>

std::atomic<int> value(0); // 原子变量

void write() {
    value.store(42, std::memory_order_relaxed); // 原子操作:无序存储
}

void read() {
    while (value.load(std::memory_order_relaxed) == 0) {
        // do something
    }
    
    std::cout << "Value: " << value.load(std::memory_order_relaxed) << std::endl;
}

int main() {
    std::thread t1(write);
    std::thread t2(read);

    t1.join();
    t2.join();

    return 0;
}

通过使用适当的内存顺序(memory_order)来进行原子操作,可以避免乱序执行带来的问题,例如使用std::memory_order_relaxed来指定无序存储。

  1. 死锁(Deadlock):
#include <atomic>
#include <mutex>
#include <thread>

std::atomic<bool> flag1(false);
std::atomic<bool> flag2(false);

std::mutex mutex1;
std::mutex mutex2;

void process1() {
    mutex1.lock(); // 获取锁1
    
    // 假设这里执行一些操作

    flag1.store(true); // 标记为已处理

    mutex2.lock(); // 获取锁2

    // 执行需要锁2的操作

    mutex2.unlock();
    mutex1.unlock();
}

void process2() {
    mutex2.lock(); // 获取锁2

    // 假设这里执行一些操作
  
    flag2.store(true); // 标记为已处理

    mutex1.lock(); // 获取锁1

    // 执行需要锁1的操作

    mutex1.unlock();
    mutex2.unlock();
}

int main() {
    std::thread t1(process1);
    std::thread t2(process2);

    t1.join();
    t2.join();

    return 0;
}

多个线程对两个互斥量(mutex)进行获取操作,可能导致死锁,std::atomic不能直接解决死锁问题,但可以用来作为线程间的通信机制。

  1. 活锁(Live Lock):
#include <atomic>
#include <thread>
#include <iostream>

std::atomic<bool> flag1(false);
std::atomic<bool> flag2(false);

void process1() {
    while (!flag2) {
        // 假设这里执行一些操作
        // 暂停一段时间,避免忙等待
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        flag1.store(true);
        // 假设这里还有其他判断逻辑
    }
}

void process2() {
    while (!flag1.load()) {
        // 假设这里执行一些操作
        // 暂停一段时间,避免忙等待
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        flag2.store(true);
        // 假设这里还有其他判断逻辑
    }
}

int main() {
    std::thread t1(process1);
    std::thread t2(process2);

    t1.join();
    t2.join();

    std::cout << "Live lock detected!" << std::endl;

    return 0;
}

两个线程互相等待对方设置标志位,但由于不断地执行操作和忙等待,两个线程无法继续前进,造成活锁。

  1. 数据竞争(Data Race):
#include <atomic>
#include <thread>
#include <iostream>

std::atomic<int> counter(0); // 原子计数器

void increment() {
    for (int i = 0; i < 1000; i++) {
        int temp = counter.load(); // 读取计数器的当前值
        counter.store(temp + 1);   // 原子操作:递增
    }
}

int main() {
    std::thread t1(increment);
    std::thread t2(increment);

    t1.join();
    t2.join();

    std::cout << "Counter: " << counter.load() << std::endl;

    return 0;
}

两个线程同时读取和修改计数器的值,使用std::atomic可以避免数据竞争,保证计数器的正确递增。

  1. 优先级反转(Priority Inversion):
#include <atomic>
#include <thread>
#include <iostream>

std::atomic<int> sharedResource(0); // 共享资源

void lowPriorityThread() {
    while (true) {
        // 低优先级任务需要访问共享资源
        while (sharedResource.load() == 0) {
            // do something
        }

        std::cout << "Low priority thread accessing shared resource." << std::endl;

        // 假设这里执行一些低优先级任务的操作

        // 完成低优先级任务后释放共享资源
        sharedResource.store(0);
    }
}

void highPriorityThread() {
    // 高优先级任务需要持有共享资源
    sharedResource.store(1);

    std::cout << "High priority thread acquired shared resource." << std::endl;

    // 假设这里执行一些高优先级任务的操作

    // 完成高优先级任务后释放共享资源
    sharedResource.store(0);
}

int main() {
    std::thread t1(lowPriorityThread);
    std::thread t2(highPriorityThread);

    t1.join();
    t2.join();

    return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1045755.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis的安装与基本使用

文章目录 Linux 环境下安装Redis下载Redis 安装包解压安装包安装Redis进入redis安装包下编译并且安装到指定目录下 启动redis配置远程访问找到Redis.config文件 Windows 环境下安装Redis说明官方提供方式安装或启用WSL2在WSL&#xff08;Ubuntu&#xff09;上安装Redis启动Redi…

【QT+CUDA】QT中使用cuda,QT+VS+cuda下载安装配置

文章目录 相关网址汇总&#xff1a; 一、软件安装&#xff1a;VS、CUDA、QT1 安装VS1.1 下载1.2 vs2017安装1.3 vs2015安装 2 安装CUDA2.1 下载2.2 安装2.3 测试2.4 卸载 3 安装QT3.1 下载3.2 安装 二、QT使用cuda1 .pro文件 三、常用操作1 NVIDIA控制面板&#xff1a;显卡、驱…

口袋参谋:如何快速补充缺失的免费流量入口?30秒就可操作!

​在淘宝店铺运营过程中&#xff0c;流量过低怎么办&#xff1f; 我相信很多卖家会选择付费流量&#xff0c;如&#xff1a;直通车、引力魔方等付费推广&#xff0c;虽然说它们的流量来的比较快&#xff0c;但是也要花大价钱去投流。 如果想免费提高店铺流量的&#xff0c;不妨…

rv1126-rv1109-烧录方法之TFTP

注意:开机按ctrl+C既可以进入uboot指令集 因为之前习惯了用RK的烧录工具,为了兼容ssd202d的烧录方法 于是我开始尝试了使用ssd202d的方法烧录 SSD202D的方法是 烧录uboot 然后用TFTP烧录下去,于是我开始尝试 烧录前三个即可,后面的img用tftp烧录,由于工作量太…

成都直播基地排名,天府蜂巢直播基地获高知名度直播基地称号

成都直播基地的排名在近年来不断攀升&#xff0c;其中成都天府蜂巢直播产业基地凭借其卓越的表现获得了高知名度的直播基地称号。成都天府蜂巢直播产业基地凭借其卓越的发展成就和优质的服务&#xff0c;力争为西部地区打造了一个独具魅力的直播产业基地。 双方携手 提速发展 …

怎么压缩word文档的大小?

怎么压缩word文档的大小&#xff1f;Word文件压缩成一个普遍存在的挑战&#xff0c;现在看来至少是这样的。最近&#xff0c;我们接到了许多用户的疑问&#xff0c;他们想知道如何压缩Word文件大小。这个问题似乎广泛存在于办公场景中&#xff0c;因此我们需要找到解决方案。导…

排序算法之【希尔排序】

&#x1f4d9;作者简介&#xff1a; 清水加冰&#xff0c;目前大二在读&#xff0c;正在学习C/C、Python、操作系统、数据库等。 &#x1f4d8;相关专栏&#xff1a;C语言初阶、C语言进阶、C语言刷题训练营、数据结构刷题训练营、有感兴趣的可以看一看。 欢迎点赞 &#x1f44d…

【ROS 2】-2 话题通信

飞书原文链接&#xff1a; Docs

文件的随机读写函数:ftell rewind

目录 函数介绍&#xff1a; ftell&#xff1a; 函数原型&#xff1a; 举例&#xff1a; 文件内容展示&#xff1a; 代码操作&#xff1a; 结果&#xff1a; rewind&#xff1a; 函数原型&#xff1a; 举例&#xff1a; 文件内容展示&#xff1a; 代码操作&#xff1…

JOSEF约瑟 静态中间继电器JZY-402 JZJ-404 AC220V 触点形式两开两闭

系列型号&#xff1a; JZY(J)-400静态中间继电器 JZ-Y-401静态中间继电器JZ-Y-402静态中间继电器 JZ-Y-403静态中间继电器JZ-Y-404静态中间继电器 JZ-Y-405静态中间继电器JZ-Y-406静态中间继电器 JZ-Y-407静态中间继电器JZ-Y-408静态中间继电器 JZ-Y-409静态中间继电器JZ…

10kv后台配电监控系统

10kv电力系统应用广泛&#xff0c;在各行各业都发挥着举足轻重的作用&#xff0c;其运行状态直接影响到电力系统的稳定性和可靠性。 一、系统概述 10kV后台配电监控系统是指对10kV配电系统的各种设备进行实时监控、调节、保护、控制和调节的现代化管理系统。通过在电力…

找高清、4K图片素材就上这6个网站,免费下载!

不会还有人找图片素材直接上网去搜吧&#xff0c;告诉你们6个网站&#xff0c;轻松找到想要的图片素材&#xff0c;不仅质量高&#xff0c;还可以免费下载&#xff0c;重点是还可以商用。赶紧收藏起来吧~ 1、菜鸟图库 https://www.sucai999.com/pic.html?vNTYwNDUx 网站主要为…

挺进欧洲:中国汽车如何破解品牌与成本双重困境?

摘要&#xff1a;2022年&#xff0c;中国超越德国&#xff0c;跻身全球第二大汽车出口大国&#xff0c;仅次于日本。历经国内市场的激烈竞争和技术积累,中国汽车品牌凭借在新能源技术上的优势和制造力,决定挑战欧洲-BBA(奔驰、宝马、奥迪)的主场。令人惊讶的是,尽管在21世纪初,…

全链路压测:优化系统性能的关键措施

在现代互联网时代&#xff0c;系统的性能稳定性和可靠性对于企业的成功至关重要。全链路压测作为一项关键的测试措施&#xff0c;可以模拟真实的负载情况&#xff0c;全面评估系统在高负载环境下的表现。本文将介绍全链路压测的定义、作用以及在优化系统性能方面的重要性。 一、…

基于R做宏基因组进化树+丰度柱状图TreeBar带聚类树的堆叠柱形图

写在前面 同之前一样&#xff0c;重分析需要所以自己找了各路代码借鉴学习&#xff0c;详情请参考 R语言绘制带聚类树的堆叠柱形图 &#xff0c; 实操效果如下&#xff1a; 步骤 表格预处理 选取不同样本属水平的物种丰度图&#xff08;绝对和相对水平都可以&#xff0c;相对…

Spring实现简单的Bean容器

1.BeanDefinition&#xff0c;用于定义 Bean 实例化信息&#xff0c;现在的实现是以一个 Object 存放对象 public class BeanDefinition {/*** bean对象*/private Object bean;/*** 存放 &#xff08;定义&#xff09;Bean 对象*/public BeanDefinition(Object bean) {this.bea…

RFID用于仓库盘点,省时省力

RFID用于仓库盘点&#xff0c;省时省力 RFID技术已经被广泛应用在工业制造和日常生活当中。它使用射频信号来识别和跟踪标签中的信息。RFID系统由两个主要组件组成&#xff1a;RFID标签和RFID读写器。RFID标签通常由一个芯片和一个天线组成。标签内的芯片存储着特定的数据&…

监控员工聊天记录违法吗?监控员工聊天记录软件

在现代社会&#xff0c;企业面临着如何确保员工工作效率和质量的挑战。为了解决这一问题&#xff0c;一些企业选择监控员工的聊天记录&#xff0c;以确保他们遵守公司规定&#xff0c;不泄露敏感信息&#xff0c;以及避免工作效率低下。然而&#xff0c;这种做法是否合法呢&…

阿里巴巴K8S集成seata

正文 在K8S集成seata&#xff0c;官方配置 代码 apiVersion: v1 kind: Service metadata:name: seata-servernamespace: wmz-devlabels:k8s-app: seata-server spec:type: NodePortports:- port: 8091nodePort: 30091protocol: TCPname: httpselector:k8s-app: seata-server-…

适合中小企业的推荐佳企业备份软件

企业环境通常比单个工作站拥有更多的机器、更多的数据和更多的人员&#xff0c;这些可能会带来更多的潜在风险——96%的企业经历过至少一种数据丢失的主要原因&#xff1a;人为错误、系统崩溃、硬件故障、病毒袭击、停电、火灾和自然灾害。 数据丢失对企业造成的损害不仅在…