原子操作 CAS

2023-11-07

参考:

https://zhuanlan.zhihu.com/p/400817892

https://www.bilibili.com/read/cv10686883/

https://blog.csdn.net/www_dong/article/details/119920236

https://blog.csdn.net/niu91/article/details/116308436

https://blog.csdn.net/CringKong/article/details/79966161

1、原子操作

原子操作,不会被线程调度机制打断的操作。这个操作一旦开始,就一直运行到结束,中间不会有任何上下文切换。

典型的原子操作有(原子操作需要硬件支持)

Load / Store :读写内存

Test and Set:针对bool变量,如果为true则返回true,如果为false,则将变量置为true并返回false。 

Clear:将bool变量设为false。 

Exchange:将指定位置的值设置为传入值,并返回其旧值。 

Compare and Swap:将指定位置的值与期望值比较,如果相等则赋值为新值,如果不等则将期望值设置为自身。返回是否设置成功。 

Fetch And 加减乘除系列:对指定位置的值使用传入参数执行加减乘除,并返回旧值。

2、竞态条件(Race Conditon)

int i = 0;
i++;

mov         eax,dword ptr [i]   // 将i加载到eax寄存器
add         eax,1  // eax中的值加一
mov         dword ptr [i],eax  // 将eax中的值赋值到i的地址 

汇编层面有三步操作:读-修改-写。线程1修改完还未写到内存中去,线程2得到CPU开始执行,修改的会是线程1未写到内存中的值,存在线程安全问题。

4、线程安全

解决线程安全通常有以下方法:

1、使用原子操作。

2、加锁:悲观锁或者乐观锁(无锁)

1、atomic_int num; num++;

在msvc中,C++11 提出的 atomic_int 类型,在 num++ 操作时,底层调用windows提供的原子自增函数_InterlockedIncrement 。CAS提供的API

2、悲观锁

mutex 就是一种悲观锁的使用。假定会发生并发冲突,屏蔽一切可能违反数据完整性的操作。

3、乐观锁

假设不会发生并发冲突,每次不加锁而是假设没有冲突而去完成某项操作,只在提交操作时检查是否违反数据完整性。

CAS (Compare And Swap)操作是一条CPU的原子指令,所以不会有线程安全问题。

CAS(addr,old,new)

解释:将addr存放的只与old比较,如果等于old,则将new赋值给addr。

C++ 可以实现如下:

bool compare_and_swap(int* pAddr, int nExpected, int nNew) {
    if (*pAddr == nExpected) {
        *pAddr = nNew;
        return true;
    }
    else 
        return false;
}

不同编译器 底层实现不一样,但算法思想一样。

GCC的CAS,GCC4.1+版本中支持CAS的原子操作。

1)bool __sync_bool_compare_and_swap (type *ptr, type oldval, type newval, ...) 
2)type __sync_val_compare_and_swap (type *ptr, type oldval, type newval, ...)

C++11中的CAS,C++11中的STL中的atomic类的函数可以让你跨平台。

template< class T > bool atomic_compare_exchange_weak( std::atomic* obj,T* expected, T desired ); 
template< class T > bool atomic_compare_exchange_weak( volatile std::atomic* obj,T* expected, T desired );

Windows的CAS

InterlockedCompareExchange ( __inoutLONGvolatile *Target,
                                __inLONGExchange,
                                __inLONGComperand);

 CAS 存在的问题: ABA。

解决方法: Double CAS,即可以加上版本号。

像状态寄存器、映射到内存地址上的 I/O 操作、涉及硬件操作的变量需要加volatile,因为对它们的每一次操作都有其意义

而并发时,多线程多任务环境下各任务间共享的标志,其实更应该用原子量和内存序,或者直接加互斥锁,以确保共享区操作的原子性和顺序性。

所以其实volatile和atomic是应用于不同场景的,甚至可以叠加使用。比如:

 volatile std::atomic<int> value;

这个式子表示对value的操作都是原子性的,

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

原子操作 CAS 的相关文章

  • 静态只读字符串数组

    我在我的 Web 应用程序中使用静态只读字符串数组 基本上数组有错误代码 我将所有类似的错误代码保存在一个数组中并检查该数组 而不是检查不同常量字符串中的每个错误代码 like public static readonly string m
  • 计算 XML 中特定 XML 节点的数量

    请参阅此 XML
  • 适合初学者的良好调试器教程[关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 有谁知道一个好的初学者教程 在 C 中使用调试器 我感觉自己好像错过了很多 我知道怎么做 单步执行代码并查看局部变量 虽然这常常给我带来问
  • 代码 GetAsyncKeyState(VK_SHIFT) & 0x8000 中的这些数字是什么?它们是必不可少的吗?

    我试图在按下按键的简单动作中找到这些数字及其含义的任何逻辑解释 GetAsyncKeyState VK SHIFT 0x8000 可以使用哪些其他值来代替0x8000它们与按键有什么关系 GetAsyncKeyState 根据文档返回 如果
  • 以编程方式读取 SQL Server 查询计划建议的 SQL 特定执行的索引?

    如果我在 SSMS 中运行此命令 set showplan xml on GO exec some procedure arg1 arg2 arg3 GO set showplan xml off GO 我获得查询执行中涉及的完整调用堆栈的
  • GetType() 在 Type 实例上返回什么?

    我在一些调试过程中遇到了这段代码 private bool HasBaseType Type type out Type baseType Type originalType type GetType baseType GetBaseTyp
  • 如何填充 ToolStripComboBox?

    我发现它很难将数据绑定到ToolStripComboBox 好像没有这个ValueMember and DisplayMember特性 怎么绑定呢 访问toolstripcombobox中包装的组合框并访问其ValueMember Disp
  • 从客户端访问 DomainService 中的自定义对象

    我正在使用域服务从 Silverlight 客户端的数据库中获取数据 在DomainService1 cs中 我添加了以下内容 EnableClientAccess public class Product public int produ
  • 使用可变参数包类型扩展的 C++ 函数调用者包装器

    我绑定了一些 API 并且绑定了一些函数签名 如下所示 static bool WrapperFunction JSContext cx unsigned argc JS Value vp 我尝试将对象和函数包装在 SpiderMonkey
  • File.AppendText 尝试写入错误的位置

    我有一个 C 控制台应用程序 它作为 Windows 任务计划程序中的计划任务运行 此控制台应用程序写入日志文件 该日志文件在调试模式下运行时会创建并写入应用程序文件夹本身内的文件 但是 当它在任务计划程序中运行时 它会抛出一个错误 指出访
  • 类型约束

    我有以下类层次结构 class Header IEnumerable
  • 使用valgrind进行GDB远程调试

    如果我使用远程调试gdb我连接到gdbserver using target remote host 2345 如果我使用 valgrind 和 gdb 调试内存错误 以中断无效内存访问 我会使用 target remote vgdb 启动
  • 如何在 Qt 应用程序中通过终端命令运行分离的应用程序?

    我想使用命令 cd opencv opencv 3 0 0 alpha samples cpp cpp example facedetect lena jpg 在 Qt 应用程序中按钮的 clicked 方法上运行 OpenCV 示例代码
  • 高效列出目录中的所有子目录

    请参阅迄今为止所采取的建议的编辑 我正在尝试使用 WinAPI 和 C 列出给定目录中的所有目录 文件夹 现在我的算法又慢又低效 使用 FindFirstFileEx 打开我正在搜索的文件夹 然后我查看目录中的每个文件 使用 FindNex
  • 在屏幕上获取字符

    我浏览了 NCurses 函数列表 似乎找不到返回已打印在屏幕上的字符的函数 每个字符单元格中存储的字符是否有可访问的值 如果没有的话Windows终端有类似的功能吗 我想用它来替换屏幕上某个值的所有字符 例如 所有a s 具有不同的特征
  • WebBrowser.Print() 等待完成。 。网

    我在 VB NET 中使用 WebBrowser 控件并调用 Print 方法 我正在使用 PDF 打印机进行打印 当调用 Print 时 它不会立即启动 它会等到完成整个子或块的运行代码 我需要确保我正在打印的文件也完整并继续处理该文件
  • C++ new * char 不为空

    我有一个问题 我在 ASIO 中开发服务器 数据包采用尖头字符 当我创建新字符时 例如char buffer new char 128 我必须手动将其清理为空 By for int i 0 i lt 128 i buffer i 0x00
  • 在 Windows Phone silverlight 8.1 上接收 WNS 推送通知

    我有 Windows Phone 8 1 silverlight 应用程序 我想使用新框架 WNS 接收通知 我在 package appxmanifest 中有
  • GCC 的“-Wl,option”和“-Xlinker option”语法之间有区别吗?

    我一直在查看一些配置文件 并且看到它们都被使用 尽管在不同的体系结构上 如果您在 Linux 机器上使用 GCC 将选项传递给链接器的两种语法之间有区别吗 据我所知 阅读 GCC 手册时 他们的解释几乎相同 From man gcc Xli
  • 如何将十六进制字符串转换为无符号长整型?

    我有以下十六进制值 CString str str T FFF000 如何将其转换为unsigned long 您可以使用strtol作用于常规 C 字符串的函数 它使用指定的基数将字符串转换为 long long l strtol str

随机推荐