C++ 里 ++i 是原子操作吗?

2023-09-21 16:25:54

1.什么是原子操作

在多线程环境下,原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何 context switch (切换到另一个线程)。

原子操作可以确保某些特定操作在多线程条件下,不会由于线程切换而导致数据污染。比如,对一个变量的读/写操作,就是一个常见的需要原子化的场景。如果把这样的读/写操作设计成原子操作,就可以避免多线程竞争导致的数据不一致问题。

2.++i 是否原子操作

在 C++ 中,对一个变量的自增(++)操作看似很简单,理论上它包含:

  • 读变量原值
  • 对原值加 1
  • 将结果写回变量

例如:

int i = 0; 
++i;

但是在多线程环境下,这三个步骤如果被打断,可能导致如下结果:

  • 线程1 读到i=0
  • 线程2也读到i=0
  • 线程1对i加1并写入,现在i=1
  • 线程2对i加1并写入,这时覆盖了线程1的写入,又使得i=1

很明显,实际的运行次数是2次,但最终结果是i=1,这就是数据污染的例子。

为了避免上述情况,C++编译器在编译过程中,会自动将一些看似简单的操作(例如自增操作)转换为原子指令,从而保证其原子性。

这种特性与具体的编译器实现相关,比如主流的GNU编译器和MSVC编译器都对自增操作进行了优化,确保其原子执行。

所以可以认为,在绝大多数C++实现中,++i这个自增操作是原子的。但是仍有一些例外情况需要注意,比如在嵌入式平台上可能需要开发者显式指定操作的原子性。

3. 如何保证操作的原子性

在不能依赖编译器优化的情况下,C++11提供了一些方法可以保证操作的原子性:

(1) atomic类型:提供了一些原子类型,对其操作天然原子

int i = 0; 
++i;

(2) mutex:使用mutex可以在临界区内执行一个原子块

std::mutex m;
m.lock();
// critical section
cnt++; 
m.unlock();

(3) lock-free编程:通过CAS(compare-and-swap)等原子指令实现非阻塞同步

atomic_int val;
int expect = val.load();
while(!val.compare_exchange_weak(expect, expect + 1)) {
  expect = val.load(); 
} atomic_int val;
int expect = val.load();
while(!val.compare_exchange_weak(expect, expect + 1)) {
  expect = val.load(); 
}

4. 总结

综上所述,在大多数普通的桌面程序和服务端程序中,++i这样的自增操作可以看作是原子的,编译器会做出优化。但是对于嵌入式开发等要求原子操作显式控制的场景,C++11提供了一些新的原子类型和同步原语来保证操作的原子执行。

更多推荐

【错误记录】Android Studio 中最新的 Gradle 配置中设置插件依赖 ( 2023 年 8 月 24 日 | 最新 Gradle 中配置插件依赖的变化 | 增加 Maven 仓库源 )

文章目录一、最新Gradle中配置插件依赖的变化二、报错信息三、增加Maven仓库源五、使用老版本方式导入插件一、最新Gradle中配置插件依赖的变化当前最新的AndroidStudio开发环境,生成的Gradle配置脚本使用了最新API,用起来不太习惯;根目录下的build.gradle构建脚本变成了下面的样式,单纯

【错误记录】Android Studio 创建 Module 模块报错 ( Cannot resolve external dependency org.jetbrains.kotlin:kotl )

文章目录一、报错信息二、解决方案目前使用的是最新的Gradle配置,创建Module生成的源码与Gradle配置出现了冲突,导致的问题;解决此类问题,要仔细检查Gradle构建脚本,排查每个依赖库的来源;本次错误就是AS系统自动成的Module修改了Gradle构建脚本,导致依赖下载失败;一、报错信息在AndroidS

亚马逊云科技打造SAP核心业务系统上云最佳实践,加快业务转型和价值实现

数字化转型步入深水区,企业竞争日益激烈,乘云而上、快速进行现代化转型和创新,才能不断紧跟趋势变化,实现「高质量发展」。作为亚马逊云科技全球战略合作伙伴,SAP和亚马逊云科技的联合创新已超过15年,双方共同为SAP客户的关键业务型工作负载提供支持,持续助力企业数字化转型。如今,全球有数以千计的企业将SAP核心业务系统运行

前端工程师路上的宝藏:不可错过的进阶必读文章!

JavaScript《javascript高级程序设计》核心知识总结必要性:⭐️⭐️⭐️⭐️难度:⭐️⭐️⭐️⭐️谏言:建议初学者先读一两遍红宝石书(即JavaScript高级程序设计),犀牛书可以暂时不看(读起来有点累)ES6入门教程必要性:⭐️⭐️⭐️⭐️⭐️难度:⭐️⭐️⭐️⭐️⭐️谏言:阮大佬的开源精品,强推!

如何在Ubuntu中挂载新硬盘

参考:如何在Ubuntu中挂载新硬盘_笔记大全_设计学院ubuntu将大于2T硬盘挂载到/home目录并使其永久生效的详细操作步骤_ubuntu挂载硬盘到home目录_菲玛的博客-CSDN博客win10+ubuntu18.04home目录扩容方法_ubuntu增加home空间_cfreeze的博客-CSDN博客1、检测

【二分法查找】

使用二分法查找需要注意的点:使用二分法的前提:数组为有序数组,同时题目还强调数组中无重复元素。二分法经常写乱,主要是因为对区间的定义没有想清楚,区间的定义就是不变量。要在二分查找的过程中,保持不变量,就是在while寻找中每一次边界的处理都要坚持根据区间的定义来操作,这就是循环不变量规则。写二分法,区间的定义一般为两种

clickhouse简单安装部署

目录前言(来源于官方文档):一.下载并上传1.下载地址:点我跳转下载2.上传至Linux二.解压和配置1.解压顺序注意:必须按照以下顺序解压,并且每解压一个都要执行该解压后文件的install/doinst.sh文件解压步骤:三.启动1.启动服务2.启动客户端四.建表语法与clickhouse数据类型1.建表语法2.数

UI设计师面试时如何自我介绍优漫动游

学会了UI设计技术,接下来的计划就是要找工作了,UI设计在面试环节的自我介绍很重要,有时候一分钟的自我介绍已经足够让HR判断出你适不适合他们公司,那做为一名UI设计师面试时如何自我介绍呢?来看看下面的详细介绍。UI设计师面试时如何自我介绍UI设计师面试时如何自我介绍:怎么做好一次自我介绍?1、自我介绍内容:结合面试岗位

什么是高阶成分(HOC)?解释 React 中 render() 的目的?

高阶成分(HOC)是一种基于React的组合特性而形成的设计模式。HOC是自定义组件,在其中包裹了另一个组件。他们可以接受任何动态提供的子组件,但不会修改或复制其输入组件中的任何行为。您可以说HOC是"纯"组件1。HOC通过对组件逻辑的重用,让组件逻辑可复用、可测试、可维护,从而提高开发效率1。在React中,rend

利用爬虫技术自动化采集汽车之家的车型参数数据

导语汽车之家是一个专业的汽车网站,提供了丰富的汽车信息,包括车型参数、图片、视频、评测、报价等。如果我们想要获取这些信息,我们可以通过浏览器手动访问网站,或者利用爬虫技术自动化采集数据。本文将介绍如何使用Python编写一个简单的爬虫程序,实现对汽车之家的车型参数数据的自动化采集,并使用亿牛云爬虫代理服务来提高爬虫的稳

Java AOP Framework概述

JavaAOPFramework概述1.AspectJ1.1使用AspectJ进行切面编程2.SpringAOP2.1使用SpringAOP进行切面编程2.2如何决定使用哪种动态代理2.3如何通过配置指定代理方式2.4SpringAOP和AspectJ的关系3.SpringBootAOP4.扩展4.1AspectJ织入

热文推荐