面试二十七、 CAS和Atomic

CAS锁机制(无锁、自旋锁、乐观锁、轻量级锁)-CSDN博客

1. ABA问题

在C++中,可以使用std::atomic和版本号来解决ABA问题。C++标准库没有直接提供类似Java的AtomicStampedReference,但可以通过将版本号和指针组合在一起实现类似的效果。

#include <atomic>
#include <iostream>
#include <thread>template <typename T>
class AtomicStampedReference {
public:AtomicStampedReference(T initial_value, int initial_stamp): value_(initial_value), stamp_(initial_stamp) {}bool compare_and_set(T expected_value, T new_value, int expected_stamp, int new_stamp) {auto current_value = value_.load();auto current_stamp = stamp_.load();return (current_value == expected_value && current_stamp == expected_stamp) &&value_.compare_exchange_strong(current_value, new_value) &&stamp_.compare_exchange_strong(current_stamp, new_stamp);}T get_reference() const {return value_.load();}int get_stamp() const {return stamp_.load();}private:std::atomic<T> value_;std::atomic<int> stamp_;
};void aba_example() {AtomicStampedReference<int> atomicStampedRef(100, 0);auto thread1 = [&atomicStampedRef]() {int stamp = atomicStampedRef.get_stamp();int reference = atomicStampedRef.get_reference();std::cout << "Thread 1 initial stamp: " << stamp << "\n";std::cout << "Thread 1 initial value: " << reference << "\n";if (atomicStampedRef.compare_and_set(reference, reference + 1, stamp, stamp + 1)) {std::cout << "Thread 1 new stamp: " << atomicStampedRef.get_stamp() << "\n";std::cout << "Thread 1 new value: " << atomicStampedRef.get_reference() << "\n";}};auto thread2 = [&atomicStampedRef]() {int stamp = atomicStampedRef.get_stamp();int reference = atomicStampedRef.get_reference();atomicStampedRef.compare_and_set(reference, reference + 1, stamp, stamp + 1);atomicStampedRef.compare_and_set(reference + 1, reference, stamp + 1, stamp + 2);std::cout << "Thread 2 stamp after ABA: " << atomicStampedRef.get_stamp() << "\n";std::cout << "Thread 2 value after ABA: " << atomicStampedRef.get_reference() << "\n";};std::thread t1(thread1);std::thread t2(thread2);t1.join();t2.join();
}int main() {aba_example();return 0;
}

2. 自旋时间长开销大

循环时间长开销大

  • 描述:当多个线程竞争同一个CAS操作时,如果一直失败,线程会不断自旋重试,造成CPU资源的浪费。尤其在高并发情况下,频繁的CAS重试会导致较高的CPU开销。
  • 解决办法:可以使用自适应自旋锁或其他锁机制来减轻自旋的开销。

        自旋锁在高竞争情况下会导致大量CPU资源浪费。可以使用自适应自旋锁或结合其他锁机制来减轻这种开销。以下是一个简单的自适应自旋锁实现示例。

#include <atomic>
#include <thread>
#include <iostream>
#include <chrono>class AdaptiveSpinLock {
public:AdaptiveSpinLock() : flag(ATOMIC_FLAG_INIT) {}void lock() {int spin_count = 0;while (flag.test_and_set(std::memory_order_acquire)) {++spin_count;if (spin_count > max_spin_count) {std::this_thread::yield(); // 出让CPUspin_count = 0;}}}void unlock() {flag.clear(std::memory_order_release);}private:static const int max_spin_count = 1000; // 自适应阈值std::atomic_flag flag;
};void spinlock_example() {AdaptiveSpinLock lock;int counter = 0;auto increment = [&lock, &counter]() {for (int i = 0; i < 1000; ++i) {lock.lock();++counter;lock.unlock();}};std::thread t1(increment);std::thread t2(increment);t1.join();t2.join();std::cout << "Final counter value: " << counter << "\n";
}int main() {spinlock_example();return 0;
}

3. 多变量原子操作

CAS只能保证单个变量的原子操作,要保证多个变量的原子操作,可以使用锁机制。以下是使用std::mutex来保证多个变量的原子操作的示例。

#include <mutex>
#include <thread>
#include <iostream>class MultiVariable {
public:void update(int a, int b) {std::lock_guard<std::mutex> lock(mtx);var1 = a;var2 = b;}void get(int& a, int& b) {std::lock_guard<std::mutex> lock(mtx);a = var1;b = var2;}private:int var1 = 0;int var2 = 0;std::mutex mtx;
};void multivariable_example() {MultiVariable mv;auto writer = [&mv]() {for (int i = 0; i < 1000; ++i) {mv.update(i, i * 2);}};auto reader = [&mv]() {for (int i = 0; i < 1000; ++i) {int a, b;mv.get(a, b);std::cout << "Read values: " << a << ", " << b << "\n";}};std::thread t1(writer);std::thread t2(reader);t1.join();t2.join();
}int main() {multivariable_example();return 0;
}

4.CAS使用注意事项
(1)CAS需要和volatile配合使用

CAS只能保证变量的原子性,不能保证变量的内存可见性。CAS获取共享变量的值时,需要和volatile配合使用,来保证共享变量的可见性

(2)CAS适用于并发量不高、多核CPU的情况

CPU多核情况下可以同时执行,如果不合适就失败。而并发量过高,会导致自旋重试耗费大量的CPU资源。

5. volatile

  • C++中的volatile:主要用于防止编译器优化,确保每次访问变量时从内存中读取最新的值,但不保证多线程环境下的内存可见性和顺序一致性。
  • 内存可见性:在C++中使用std::atomic和内存序列来确保多线程环境下的内存可见性和顺序一致性。

5.atomic

【超详解】C++原子变量atomic,全面解密!_c++ atomic-CSDN博客

atomic的底层实现 - 王的博客 - 博客园 (cnblogs.com)

CPU多核同步原语 - 知乎 (zhihu.com)

深入解析现代C++中的原子(std::atomic)-51CTO.COM

std::atomic通过硬件提供的原子指令实现无锁的原子操作,确保在多线程环境下的数据一致性和线程安全。通过使用内存顺序,可以进一步控制操作的可见性和顺序,以满足不同的并发编程需求。

 

 

 

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

PWN-栈迁移

栈迁移 题目&#xff1a;BUUCTF在线评测 (buuoj.cn) 知识点&#xff1a;栈迁移 使用情况&#xff1a;题目中有栈溢出&#xff0c;但是 栈溢出的范围 有限&#xff0c;导致构造的ROP链不能完全写入到栈中&#xff0c;此时需要进行栈迁移&#xff0c;将栈迁移到能接受更多数据的…

基于51单片机的电子时钟设计

在单片机技术日趋成熟的今天&#xff0c;其灵活的硬件电路和软件电路的设计&#xff0c;让单片机得到广泛的应用&#xff0c;几乎是从小的电子产品&#xff0c;到大的工业控制&#xff0c;单片机都起到了举足轻重的作用。单片机小的系统结构几乎是所有具有可编程硬件的一个缩影…

OpenAI 的 GPT-4o 是目前最先进的人工智能模型!如何在工作或日常生活中高效利用它?

OpenAI 的 GPT-4o 是目前最先进的人工智能模型&#xff01;如何在工作或日常生活中高效利用它&#xff1f; 博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大…

oracle 12c DB卸载流程

1.运行卸载程序 [rootprimary1 ~]# su - oracle [oracleprimary1 ~]$ cd $ORACLE_HOME/deinstall [oracleprimary1 deinstall]$ ./deinstall Checking for required files and bootstrapping ... Please wait ... 这里选择3 、回车、y、y、回车、ASM 这里输入y 2.删除相关目录…

C# TcpClient

TcpClient 自己封装的话&#xff0c;还是比较麻烦的&#xff0c;可以基于线程&#xff0c;也可以基于异步写&#xff0c;最好的办法是网上找个插件&#xff0c;我发现一个插件还是非常好用的&#xff1a;STTech.BytesIO.Tcp 下面是这个插件作者的帖子&#xff0c;有兴趣的可以…

迅为RK3562开发板专为3562编写10大分类2900+页文档

iTOP-3562开发板采用瑞芯微RK3562处理器&#xff0c;内部集成了四核A53Mali G52架构&#xff0c;主频2GHZ&#xff0c;内置1TOPSNPU算力&#xff0c;RK809动态调频。支持OpenGLES1.1/2.0/3.2、0penCL2.0、Vulkan 1.1内嵌高性能2D加速硬件。 内置独立NPU, 算力达 1TOPS,可用于轻…

软件架构设计属性之5:可维护性属性分析与应用

文章目录 引言一、可维护性定义和重要性1.1 定义1.2 重要性 二、可维护性关键要素2.1 模块化2.2 单一职责2.3 低耦合2.4 高内聚2.5 抽象和封装2.6 实践建议 三、设计原则3.1 开闭原则3.2 依赖倒置原则3.3 评估方法3.4 挑战与解决方案 四、实战应用总结 引言 在当今数字化飞速发…

一篇文章讲透数据结构之树

一.树 1.1树的定义 树是一种非线性的数据结构&#xff0c;它是有n个有限结点组成的一个具有层次关系的集合。把它叫做树是因为它看起来像一棵倒挂的树&#xff0c;也就是说它是根在上&#xff0c;叶在下的。 在树中有一个特殊的结点&#xff0c;称为根结点&#xff0c;根结点…

MySQL--主从复制

目录 一、主从复制原理 1.简要原理 2.涉及到的文件 3.涉及到的线程 4.主从复制执行步骤&#xff08;重点&#xff09; 二、主从复制搭建 1.准备两台以上的数据库实例&#xff0c;要求数据库版本一致 2.区分不同角色 3.主库开启二进制日志 4.主库创建专用复制用户&…

文件IO(三)

文件IO&#xff08;三&#xff09; 左移右移Linux的man 手册文件IO打开文件操作文件关闭文件 caps lock开灯关灯读取按键文件IO操作目录文件打开目录文件操作目录文件 库动态库和静态库的优缺点创建静态库创建动态库 按下右ctrl键 亮灭灯 左移右移 Linux的man 手册 文件IO 打开…

【计算机毕设】基于SpringBoot的教师工作量管理系统设计与实现 - 源码免费(私信领取)

免费领取源码 &#xff5c; 项目完整可运行 &#xff5c; v&#xff1a;chengn7890 诚招源码校园代理&#xff01; 1. 研究目的 随着高校规模的扩大和教学任务的增加&#xff0c;教师的工作量管理变得越来越复杂和重要。传统的教师工作量管理方式效率低下&#xff0c;容易出错&…

真机调试 Error:系统错误,xxx exceed max limit 2MB

我们在使用微信开发者工具开发小程序、小游戏等应用时&#xff0c;往往会点击“真机调试”&#xff0c;微信扫描查看真实情况。 但是会出现下面的报错提示&#xff0c;是因为主包体积超过了2MB。 小程序有体积和资源加载限制&#xff0c;在微信小程序中&#xff0c;每个包不能…

Java事务入门:从基础概念到初步实践

Java事务入门&#xff1a;从基础概念到初步实践 引言1. Java事务基础概念1.1 什么是事务&#xff1f;1.2 为什么需要事务&#xff1f; 2. Java事务管理2.1 JDBC 的事务管理2.2 Spring 事务管理2.2.1 Spring JDBC2.2.1.1 添加 Spring 配置2.2.1.2 添加业务代码并测试验证 2.2.2…

【图解IO与Netty系列】Reactor模型

Reactor模型 Reactor模型简介三类事件与三类角色Reactor模型整体流程 各种Reactor模型单Reactor单线程模型单Reactor多线程模型主从Reactor模型 Reactor模型简介 Reactor模型是服务器端用于处理高并发网络IO请求的编程模型&#xff0c;与传统的一请求一线程的同步式编程模型不…

ssm汉服文化平台网站

博主介绍&#xff1a;✌程序员徐师兄、8年大厂程序员经历。全网粉丝15w、csdn博客专家、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;…

【NumPy】深入了解NumPy的multiply函数:高效矩阵和数组乘法指南

&#x1f9d1; 博主简介&#xff1a;阿里巴巴嵌入式技术专家&#xff0c;深耕嵌入式人工智能领域&#xff0c;具备多年的嵌入式硬件产品研发管理经验。 &#x1f4d2; 博客介绍&#xff1a;分享嵌入式开发领域的相关知识、经验、思考和感悟&#xff0c;欢迎关注。提供嵌入式方向…

区块链合约开发流程

区块链合约开发&#xff0c;尤其是以太坊智能合约开发&#xff0c;是一个多步骤的过程&#xff0c;从需求分析到部署和维护&#xff0c;每一步都需要仔细规划和执行。以下是详细的开发流程。北京木奇移动技术有限公司&#xff0c;专业的软件外包开发公司&#xff0c;欢迎交流合…

NextJs 数据篇 - 数据获取 | 缓存 | Server Actions

NextJs 数据篇 - 数据获取 | 缓存 | Server Actions 前言一. 数据获取 fetch1.1 缓存 caching① 服务端组件使用fetch② 路由处理器 GET 请求使用fetch 1.2 重新验证 revalidating① 基于时间的重新验证② 按需重新验证revalidatePathrevalidateTag 1.3 缓存的退出方式 二. Ser…

Window下VS2019编译WebRTC通关版

这段时间需要实现这样一个功能&#xff0c;使用WebRTC实现语音通话功能&#xff0c;第一步要做的事情就是编译WebRTC源码&#xff0c;也是很多码友会遇到的问题。 经过我很多天的踩坑终于踩出来一条通往胜利的大路&#xff0c;下面就为大家详细介绍&#xff0c;编译步骤以及踩…

【React篇】简述React-Router 的实现原理及工作方式

React Router 路由的基础实现原理分为两种&#xff0c;如果是切换 Hash 的方式&#xff0c;那么依靠浏览器 Hash 变化即可&#xff1b;如果是切换网址中的 Path&#xff0c;就要用到 HTML5 History API 中的 pushState、replaceState 等。在使用这个方式时&#xff0c;还需要在…