Is ++i an atomic operation in C++?

1.What is an atomic operation?

In a multi-threaded environment, atomic operations refer to operations that will not be interrupted by the thread scheduling mechanism; once this operation starts, it will run until the end without any context switch (switch to another thread) in the middle.

Atomic operations can ensure that certain operations will not cause data pollution due to thread switching under multi-threaded conditions. For example, reading/writing operations on a variable is a common scenario that requires atomization. If such read/write operations are designed as atomic operations, data inconsistency problems caused by multi-thread competition can be avoided.

2.++i Is it an atomic operation?

In C++, the increment (++) operation on a variable seems very simple. In theory, it includes:

  • Read the original value of the variable
  • Add 1 to original value
  • Write the result back to a variable

For example:

int i = 0; 
++i;

However, in a multi-threaded environment, if these three steps are interrupted, the following results may occur:

  • Thread 1 reads i=0
  • Thread 2 also read i=0
  • Thread 1 adds 1 to i and writes, now i=1
  • Thread 2 adds 1 to i and writes it, which overwrites thread 1's write and makes i=1

Obviously, the actual number of runs is 2, but the final result is i=1, which is an example of data pollution.

In order to avoid the above situation, the C++ compiler will automatically convert some seemingly simple operations (such as auto-increment operations) into atomic instructions during the compilation process, thereby ensuring their atomicity.

This feature is related to the specific compiler implementation. For example, the mainstream GNU compiler and MSVC compiler have optimized the auto-increment operation to ensure its atomic execution.

Therefore, it can be considered that in most C++ implementations, the increment operation ++i is atomic. However, there are still some exceptions that need to be noted. For example, on embedded platforms, developers may be required to explicitly specify the atomicity of operations.

3. How to ensure the atomicity of operations

When compiler optimization cannot be relied upon, C++11 provides some methods to ensure atomicity of operations:

(1) Atomic type: Provides some atomic types for operating natural atoms

int i = 0; 
++i;

(2) mutex: Use mutex to execute an atomic block in a critical section

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

(3) Lock-free programming: Non-blocking synchronization is achieved through atomic instructions such as 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. Summary

To sum up, in most ordinary desktop programs and server programs, increment operations such as ++i can be regarded as atomic, and the compiler will make optimizations. However, for scenarios such as embedded development that require explicit control of atomic operations, C++11 provides some new atomic types and synchronization primitives to ensure atomic execution of operations.

Guess you like

Origin blog.csdn.net/pantouyuchiyu/article/details/133137281