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

开发
对于嵌入式开发等要求原子操作显式控制的场景,C++11提供了一些新的原子类型和同步原语来保证操作的原子执行。​

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提供了一些新的原子类型和同步原语来保证操作的原子执行。

责任编辑:赵宁宁 来源: 鲨鱼编程
相关推荐

2011-07-20 14:33:19

C++IO

2014-01-09 09:45:41

原子飞原子

2022-07-01 11:07:56

int原子操作代码

2022-09-09 15:32:46

intC语言原子操作

2021-04-01 17:36:30

鸿蒙HarmonyOS应用开发

2011-05-24 17:20:57

程序员

2023-10-04 00:38:30

C++原子

2016-10-20 16:07:11

C++Modern C++异步

2015-09-07 13:55:53

PHPC++

2022-08-22 15:32:59

C++C代码

2010-01-28 11:16:28

C++操作符

2024-04-07 09:59:42

C++并发编程开发

2011-06-17 16:09:04

freadfwrite

2010-01-21 10:23:53

C++代码

2020-11-27 06:44:22

原子加锁x86

2010-01-28 15:38:57

学习C++意义

2024-06-24 12:41:40

2023-12-13 10:08:59

C++原子代码

2010-02-02 17:08:22

2010-01-27 11:00:17

C++操作符
点赞
收藏

51CTO技术栈公众号